Skip to main content
The API grows additively — new fields, parameters, and endpoints never break an existing call. Breaking changes are rare, announced ahead of time, and called out here explicitly.

September 2026

Sponsors resolve to companies by declared domain, then by ad majority

Every advertising surface that links a sponsor to a company — sponsor leaderboards, per-podcast and per-publisher advertising, sponsor detail and placements, and sponsor prospecting — now resolves the link from the sponsor’s declared domain when it maps to one live company, else from the company most of its ads that aired in the last 180 days were attributed to, else from the all-time majority. Previously the company on the sponsor’s single newest ad won, so one mis-attributed read could relabel a sponsor for every surface. Prospecting rows gain company_contested, true when the recent majority is under 60%, when the declared domain does not map to a company, when a mapped domain is contradicted by an ad majority of 60% or more, and for every all-time fallback; a weak disagreement with a mapped domain is not contested; such rows keep their best-guess company but no longer carry contacts.

x402 discovery lists every MCP tool price

/.well-known/x402.json on both hosts now itemizes the MCP server’s per-tool prices under mcp.tools — one row per payable tool with its name, summary, price and network, next to the REST endpoint rows — and the MCP host’s manifest at mcp.particle.pro/.well-known/x402 carries the same list under tools on its MCP resource (the API host’s manifest lists only its own REST resources). The mcp.price range summary stays for readers that take a single string. GET /v1/podcasts/{id}/recommendations/sponsors (and include=recommended_sponsors on podcast detail and particle_podcast_resolve) now folds sponsor records that resolve to the same company or name into one row, cites the most related venues in via rather than the heaviest buyers, counts a publisher’s sibling feeds once, and caps the weight of ad volume on any one venue. Scores spread across the range again, so band separates advertisers present across several related publishers from single-venue overlaps; exclude_top_advertisers remains the way to look past the largest programmatic advertisers. GET /v1/podcasts/{id}/related, include=related on podcast detail, and particle_podcast_list_related now list only shows above a relatedness floor, so a show with too few embedded episodes gets a short or empty list instead of fifty near-zero “weak” rows. Feeds the screens flag as machine-generated or farmed (synthetic voice and script, text read by a synthetic voice, syndication spam, brand impersonation) are never listed as related shows, and a publisher’s duplicate feeds of one show — an “all shows” aggregate, a regional edition — appear once. An empty first page carries a coverage note (not_computed, none_above_floor or filtered_out) so a client can tell “not yet” from “nothing” from “your filters”. Recommended guests and sponsors, which read the same related sets, inherit the floor and the screening.

Related episodes: one story, many shows

GET /v1/podcasts/episodes/{id}/related and particle_podcast_list_related_episodes now carry at most two episodes per show, collapse the same content republished on another feed (including the query episode itself on a sibling feed) to one row, and exclude shows the AI and farm screens have flagged as machine-generated, machine-read, syndication spam or brand impersonation. Episodes of shows removed from the catalogue no longer appear with an empty podcast.

Pitch lists seed from bookings, once per publisher

GET /v1/podcasts/guests/{id}/recommendations/podcasts, and include=recommended_podcasts on particle_podcast_get_guest, now seed from the shows that booked the person as a guest or panelist; segments a correspondent files for their own newsroom no longer count, and a publisher’s sibling feeds count as one venue. A Bloomberg anchor’s pitch list was Bloomberg’s other feeds, and one appearance on an outlet with a republication feed scored as two. Scores and via follow: via still lists every seed show, strongest first.

Entity charts leave beta

GET /v1/podcasts/entities/charts and GET /v1/podcasts/entities/charts/{category} are generally available at the standard tier. Each day we publish an edition of five charts. The per-category mention charts tv-series, movies, and video-games rank entities by the distinct podcasts that named them in ordinary dialogue; the composite all draws on a wider curated pool of entity types (people, books, music, brands, organizations, sports, events, products, and more) and ranks by a blend of that reach and 52-day elevation, so its rank does not follow podcast_count; guests (source: guest_appearances) ranks people by the distinct shows they appeared on. Every entry carries its movement since the previous edition. Mention-chart entries on 7d editions also carry a momentum classification with the window averages behind it (signals is omitted on the guests chart and on 24h editions), and any entry may carry a one-line why, usually backed by a verbatim quote (quote is omitted when none was captured) — why itself is omitted when there is nothing notable to explain. While the quote’s source episode still exists (why.verifiable), the receipt carries the episode and the offsets to play it from; once it has been deleted, the episode fields are omitted and only the text and quote remain. Guest entries carry their in-window appearances instead, plus a highlight_clip when one of those appearances has a scored clip. include=timeseries adds each entry’s daily history as dated buckets; include=driven_by groups derivative entries under the entry driving them; sort=acceleration surfaces the fastest risers from anywhere in a mention chart. The category charts are volume-ranked, not acceleration-ranked.

Agent onboarding playbook and a complete endpoint map

api.particle.pro/agents.md (also at mcp.particle.pro/agents.md, and as the Agent onboarding page) is the playbook an agent follows to connect a client over MCP, the REST API, or x402 and run a first live query: which surface to pick, the configuration for each client, the restart most clients need before new tools appear, and what to do when something breaks. Paste “Read https://api.particle.pro/agents.md and connect me to Particle” into any coding agent. api.particle.pro/llms.txt maps every public endpoint and MCP tool to its documentation page and keyless per-call price, with the conventions (identifiers, pagination, include=, time, language, rate limits, errors) in one file. It is rendered from the endpoint and tool catalogs, so it is complete by construction. The docs index at docs.particle.pro/llms.txt now lists every page, and the agent conventions it opens with are the same text the MCP server sends on initialize. The /.well-known/x402 manifest links to all three, and the API reference introduction gathers the conventions in one table, including the rate limit: 10,000 requests per minute per organization. GET /v1/podcasts/{id}/recommendations/guests, and include=recommended_guests on podcast detail and particle_podcast_resolve, no longer reward how often a person recurs on a single related show, and a publisher’s sibling feeds now count once. Both mechanisms had let a news outlet’s own correspondents — who file segments on their newsroom’s feeds every week — outrank guests that several independent shows had chosen. Scores spread across the range instead of piling up near 1.0 and ties rank deterministically; because one publisher counts once, strong is reachable only for people booked by at least two related publishers.

Pay per request with x402 — keyless access for agents

Every standard and premium /v1/* endpoint now accepts an x402 USDC micropayment in place of an API key. A keyless request returns 402 with the payment requirements in a PAYMENT-REQUIRED header and a payment_required body; the agent signs the transfer, retries with PAYMENT-SIGNATURE, and receives the response plus a PAYMENT-RESPONSE receipt. Every price is a whole number of cents per call, from $0.01, and is listed per endpoint and per MCP tool at api.particle.pro/llms.txt; failed requests are never charged. Nothing changes for keyed traffic: any presented credential — even an invalid one — takes the normal authentication path. Free endpoints stay free, and alerts and enterprise surfaces still require an account.

Buy-side recommendations for advertisers

GET /v1/companies/{id}/podcast/recommendations (premium) lists the shows a company could advertise on next: every show it has never bought, ranked by relatedness to the shows its sponsors already run ads on, each weighted by how much it advertises there. include=via attaches the company’s own shows behind each pick; min_score and language filter. In the temporal holdout it recovered advertisers’ later buys at 13× the rate of category-by-popularity. MCP: include: ["podcast_recommendations"] on particle_company_get. GET /v1/podcasts/{id}/recommendations/sponsors gains include=contacts — up to three people at each recommended sponsor’s company most likely to own a sponsorship decision (marketing, partnerships, brand, media and advertising, communications, growth, then other executives), each with a title, a role class and a LinkedIn URL when known — and exclude_top_advertisers=N, which drops the N most active advertisers corpus-wide so the list shows the long tail where it is strongest. GET /v1/companies/{id}/people gains role= with the same classes (plus other), matching the classifier’s first-match rule. GET /v1/podcasts/{id}/related and particle_podcast_list_related gain catalogue filters: language, publishing_status, suitability_tier, min_popularity and exclude_same_publisher, applied to the stored ranking with pagination over the filtered list.

GET /v1/podcasts/episodes/lookup — resolve a hosting platform’s episode ID

platform=megaphone, omny, acast and art19 resolve a podcast host’s own episode ID. The ID is read from the episode’s audio URL, so coverage is simply whether that episode is served by that host, and an episode resolves under exactly one of them. Pass the bare ID rather than the audio URL: publishers route audio through arbitrary stacks of tracking prefixes, so the URL you hold may not resemble the one we stored, but the ID inside it is stable.

GET /v1/podcasts/episodes/lookup — resolve an RSS guid or PodcastIndex episode ID

platform=guid resolves the <guid> an episode carries in its publisher’s feed — the podcast ecosystem’s canonical episode identifier, and the broadest coverage of any identifier on this endpoint at roughly 89% of episodes. Guids are matched exactly as supplied and never parsed, since publishers use everything from UUIDs to permalink URLs. A guid identifies an episode within its feed rather than globally, so for the ~1.4% that reach more than one episode the lookup returns the one on the most popular podcast, deterministically. platform=podcastindex resolves a PodcastIndex episode ID. These are numeric and unique, so no tie-breaking applies; a non-numeric value is reported as a miss rather than failing the whole request.

GET /v1/podcasts/{id}/related — shows like this show, with the reasons

A new standard-tier GET /v1/podcasts/{id}/related returns the shows most related to a podcast, best first. Each result carries a calibrated score in (0, 1], a coarse bandstrong (same beat and audience), moderate (overlapping subject or audience), weak (a loose connection) — and, with include=basis, a basis object naming the signals behind the pairing: content_similarity of recent episodes, topic_overlap with the top shared_topics, shared_guest_count with named shared_guests, same_publisher, shared_sponsor_count, and shared_category_count. Branch on band; the score’s exact thresholds may be recalibrated as the ranker improves. Related sets are precomputed per show from its embedded transcripts, topic profile, guest roster, network, and advertisers, restricted to the show’s language, and refreshed as new episodes land, so the endpoint is a fast page read. A show whose set has not been computed yet returns 200 with an empty data array, not 404. GET /v1/podcasts/{id} gains include=related for the five most related shows inline in lean form. The ranker was validated against Apple’s “You Might Also Like” lists, a temporal shared-guest holdout, and a calibrated editorial judge; the measured recall and precision figures are published alongside the methodology once the full-catalogue evaluation completes.

GET /v1/podcasts/episodes/{id}/related — episodes elsewhere covering the same story or subject

A new standard-tier GET /v1/podcasts/episodes/{id}/related returns episodes from other shows that cover the same story or subject as an episode, best first. Each result carries a score, a band, and with include=basis the reasons: content similarity, shared entities, shared topics, a shared news story, shared guests, and days apart. published_within_days narrows to a recency window (news-style use) and same_podcast=true opts the show’s own episodes in. Results are computed live from the episode’s content centroid and reranked on the structured signals, then cached, so first calls are slower than repeats. MCP: particle_podcast_list_related_episodes.

GET /v1/podcasts/{id}/recommendations/sponsors — advertisers that buy shows like this one

A new premium-tier GET /v1/podcasts/{id}/recommendations/sponsors is the prospecting list for a show selling its own inventory: sponsors that run on the podcast’s related shows but not on this one, ranked by how related those venues are, how much the sponsor buys there, and how recently. Each row carries the sponsor with its linked company (the path to people to contact), a score and band, shared_show_count, total_ads, last_ad_at, and with include=via the related shows that run it. active_since keeps only sponsors still buying after a date. The podcast detail embeds the first five with include=recommended_sponsors. MCP: include: ["recommended_sponsors"] on particle_podcast_resolve.

GET /v1/podcasts/guests/{id}/recommendations/podcasts and GET /v1/podcasts/{id}/recommendations/guests — the pitch list and the booking pipeline

Two standard-tier views derived from related sets and guest rosters. GET /v1/podcasts/guests/{id}/recommendations/podcasts lists shows a person has not appeared on, ranked by relatedness to the shows they have, with include=via naming the person’s own shows behind each recommendation. GET /v1/podcasts/{id}/recommendations/guests lists people who have guested on the show’s related shows but never on this one, with circuit regulars excluded and include=via naming the venues. Both return 200 with an empty data array until the relevant related sets have been computed. The podcast detail embeds the first five recommended guests with include=recommended_guests.

GET /v1/podcasts/episodes/lookup — resolve a YouTube video ID

platform=youtube now resolves a YouTube video to the episode it belongs to, alongside the existing apple / itunes support. Pass a bare video ID or a full URL — watch?v=, youtu.be/, /live/, /shorts/ and /embed/ forms are all accepted. Coverage reflects the videos we have already discovered for an episode (feed links, Podcasting 2.0 alternate enclosures, Media RSS, show-note URLs, channel matching) rather than a per-episode lookup against YouTube, so every video we hold resolves — there is no partial-window caveat like Apple’s. Where one video is attached to several episodes, the lookup returns the episode whose video came from the most precise discovery method — a feed enclosure or a runtime-validated channel match ahead of a URL scraped from show notes — with podcast popularity breaking ties, so repeated calls agree.

August 2026

GET /v1/podcasts/episodes/lookup — resolve an Apple Podcasts episode ID

A new standard-tier GET /v1/podcasts/episodes/lookup turns an Apple Podcasts episode identifier — the ?i= value in an Apple Podcasts URL — into a Particle episode. It is the episode-level counterpart to GET /v1/podcasts/lookup and takes the same shape: one platform, up to 100 identifiers, one echoed result per input, with the episode key omitted for identifiers that don’t resolve. Each hit carries the parent podcast, so one call tells you both the episode and its show. A full Apple Podcasts episode URL is accepted in place of a bare ID. Coverage is partial by design: Apple only publishes episode IDs through a show’s catalog listing, which is capped at that show’s 200 most recent episodes, so deep back-catalog episodes have no Apple ID to match. Recent episodes and small-to-mid shows resolve almost always. Treat a miss as a normal outcome and fall back to GET /v1/podcasts/episodes/search.

GET /v1/podcasts/advertising/leaderboard/preview — the leaderboard’s open teaser

A new standard-tier GET /v1/podcasts/advertising/leaderboard/preview returns the top 10 advertising sponsors of the trailing 7 days for a chosen metric. Each row carries movement versus the equivalent 7-day window ending 30 days ago, in the same previous_rank / movement / rank_change vocabulary as the entity charts. Every caller gets the same board regardless of plan, so it needs no premium access — use it for public or signed-out surfaces, and GET /v1/podcasts/advertising/leaderboard (premium) for deeper pages, other time windows, and company or publisher filtering.

GET /v1/entities/search — new contained match quality

match_quality gains a value: contained marks a result whose whole name appears inside a longer query — a sign the query names that entity plus something else (spacex elon musk returns both Elon Musk and SpaceX as contained). These results rank alongside strong matches with popularity breaking ties, but don’t auto-select one: the unmatched remainder of the query usually names a second entity or a qualifier, so let the user pick. Existing integrations that only branch on exact are unaffected.

July 2026

Speaker roles — a documented, closed set (breaking change)

role on a speaker is now guaranteed to be one of fourteen values, listed in full under Episodes → Speaker roles. Previously the field could carry any of ~130 values, including one-offs and typos produced during transcript analysis; those are now normalized into the documented buckets on the way out (a REPORTER is served as CORRESPONDENT, a CALLER as AUDIENCE).
  • The episode list endpoints now apply the same default role filter as the detail endpoint. GET /v1/podcasts/episodes and GET /v1/podcasts/{id}/episodes previously embedded every speaker, while GET /v1/podcasts/episodes/{id} returned only conversation participants — the same episode gave two different answers. Both now return HOST, GUEST, PANELIST and CORRESPONDENT. If you were reading advertisers or soundbite speakers out of a list response, read them from GET /v1/podcasts/episodes/{id}/speakers?role=… instead.
  • role accepts every documented value. soundbite_speaker, narrator, announcer, voice_actor, moderator, audience, producer and other are now valid filter values; previously several roles appeared in responses but could not be filtered for.
  • role=unknown now works as documented. It returns speakers we could not identify, whether the underlying row records that explicitly or carries no role at all. It previously matched only the latter, missing almost every such speaker.
  • Role values are case-insensitive. ?role=HOST, ?role=host and ?role=HoSt are equivalent, on the REST API, in MCP tool arguments, and in alert speaker_roles. Responses continue to use uppercase. Alert speaker_roles previously rejected lowercase with a 422.
  • CO_HOST is retired. It was never produced after May 2026 and is now folded into HOST. Filtering on co_host is no longer accepted; use host, which covers both.

POST /v1/alerts/{id}/test-webhook — send a test webhook

A new POST /v1/alerts/{id}/test-webhook fires a synthetic alert.match.created delivery to every webhook connection on an alert, so you can verify your endpoint — receipt, signature, and parsing — without waiting for a real match. The payload is byte-for-byte shaped like a live delivery and carries the alert’s real id, title, kind, and first watched entity, with illustrative sample match content.
  • The webhook envelope gains a test boolean, set to true only on these test deliveries and absent on real matches, so your handler can exercise its parse-and-verify path while skipping real side effects. Existing consumers are unaffected — real matches never carry the field.
  • Nothing is persisted: a test never appears in the alert’s delivery log. The response reports each connection’s outcome (delivered, status_code, duration_ms, error); a non-2xx or unreachable endpoint comes back as delivered: false while the request itself still returns 200.
See Alerts → Webhooks → Send a test event.

June 2026

Alert filters — narrow what an alert surfaces

POST /v1/projects/{projectId}/alerts and PATCH /v1/alerts/{id} now accept a filters object that narrows matches after detection — applied identically to the matches list, the realtime email, and the daily/weekly digests. Four axes:
  • languages — restrict to source episodes in specific BCP-47-like language tags (e.g. ["en", "pt-BR", "zh-Hant"]). Empty (the default) keeps every language.
  • relevanceEVERYTHING (default) returns on-target and incidental matches; RELEVANT narrows to on-target only, dropping passing mentions.
  • source_popularityANY (default) keeps every match; POPULAR keeps only matches from the top 5% of currently-charting podcasts.
  • speaker_rolesPODCAST_SPEAKER alerts only. Replaces the default appearance set (GUEST, PANELIST, CORRESPONDENT, AUDIENCE, SOUNDBITE_SPEAKERHOST excluded). Sending this on an ENTITY_MENTION alert returns 422.
A PATCH with filters replaces the alert’s filter set wholesale — there’s no per-axis merge, so to add languages to an alert that already has relevance: "RELEVANT" you must send both. Send "filters": {} to clear every filter at once, or omit filters from the patch body to leave the existing set unchanged. Filters that narrow what gets detected (languages, speaker_roles) also gate the post-create historical backfill, so older matches that wouldn’t have passed the current filter were never stored — broadening either axis later won’t surface them. relevance and source_popularity are applied at read/delivery time, so the matches list and emails immediately reflect the change. Existing alerts keep their current behavior (no filters → every match surfaced). See Alerts → Filtering matches for the full spec and examples.

GET /v1/entities/search — purpose-built entity search (breaking change)

A new GET /v1/entities/search ranks people, companies, and knowledge graph entities for a free-text query — a name, partial name, nickname, stock ticker, @handle, or website domain — in one call. Each result is a discriminated union: type (person, company, or knowledge_graph_entity) says which kind it is, and exactly one of person/company/knowledge_graph_entity holds the matched record in the same compact shape those resources return elsewhere. Alongside it, match_quality and a mentions object (podcast_episodes, news_articles) help confirm the hit. Take the embedded record’s id (or slug) to the matching resource endpoint, or its id to alert creation.
  • The q= parameter on GET /v1/entities has been removed. Move name search to /v1/entities/search — passing q to GET /v1/entities now returns a 422 directing you there. GET /v1/entities still lists the most-mentioned entities and filters by type, podcast_id, or ids.
  • Search results are union-shaped (a person, company, or knowledge graph entity) rather than the always-an-entity shape GET /v1/entities returns — a match may be a person or company with no standalone entity, and vice versa.

GET /v1/podcasts/search now searches podcasts (breaking change)

As announced, GET /v1/podcasts/search now does what its name says: it searches podcasts by name and returns a ranked, paginated list of podcast objects. See Search.
  • If you used it to search episode dialogue (semantic_search / keyword_search), migrate to GET /v1/podcasts/episodes/search — the parameters and response shape are identical to what /v1/podcasts/search returned before this change.
  • GET /v1/podcasts?q=… keeps working and returns the same results as the new /v1/podcasts/search — it remains supported for backwards compatibility, but /v1/podcasts/search is now the canonical way to search podcasts, and the q parameter on /v1/podcasts may be retired in a future version.
  • Podcast search is metered as a standard endpoint — searching the catalog at /v1/podcasts/search now costs the same as /v1/podcasts.

Missing something? Email api@particle.pro.