October 2026
Transcription requests cost $0.05 on every plan
A transcription request that queues an episode now costs **$0.05 per episode on every plan**, down from $1.00 (Individual), $0.50 (Business) and $0.25 (Enterprise). It is still charged in USD, never drawn from your included requests, so it still needs overage billing turned on and a payment method on file (Enterprise contracts are invoiced and need none). Repeat requests answered200 are still billed at the lookup price, and a request that fails is still refunded. Existing subscriptions moved to $0.05 on October 30; a discount or spending cap your subscription already has still applies to it.
Book charts rank by rise
The book charts (fiction, nonfiction, comics-manga) now rank the same way as the league and composite charts: by how far each book or series rose above its usual coverage, instead of by podcast_count. A title that is mentioned on many shows every week no longer leads only because of its steady coverage, and a new release can lead a chart while its count is still small. Titles that did not rise follow by podcast_count. The response shape is unchanged. See how the charts rank.
League and composite charts rank by rise
The league charts (nfl, nba, and the rest of the sports group) and the composite charts (all, all-entertainment, all-sports) now rank each entity by how far it rose above its usual coverage, beyond the swing that is normal for that chart, instead of a blend of reach and 52-day elevation. The five largest rises on the last complete day lead each chart, and entities that did not rise follow by podcast_count. On the 24h editions the rise is the last day’s. The composite charts publish at most 150 entries. The response shape is unchanged. See how the charts rank.
Search lines by speaker role
/v1/podcasts/episodes/search takes speaker_role: guest, panelist, host, correspondent, or speaker for any of them. It keeps only the lines spoken in those roles, so speaker_role=guest,panelist leaves out the hosts’ lines, such as a news show’s scripted reads. Every page now names the segment types it searched in segment_types, and the roles kept in speaker_roles. Combinations that could only come back empty return 422 instead: a speaker who has never spoken in the named roles, the guest_ filters with roles that name neither guest nor panelist, and a characteristics filter with a type whose segments carry no labels.
Non-Fiction chart
GET /v1/podcasts/entities/charts lists a third book chart in the books group: nonfiction ranks memoirs, biographies, history, science, self-help, and other factual books and book series by the distinct shows that mentioned them. It publishes 7d only.
Book charts
GET /v1/podcasts/entities/charts lists two book charts in a new books group: fiction ranks novels, story and poetry collections, and fictional book series, and comics-manga ranks comic book, graphic novel, and manga series, each by the distinct shows that mentioned them. Both publish 7d only. group gains the value books.
Share of voice answers faster
/v1/podcasts/mentions/share-of-voice now picks each episode’s latest reading through a lookup that reloads every few minutes, instead of comparing every reading. On our production checks a warm call’s median fell from about 0.9 s to 0.5 s, and its slowest tenth from about 4.1 s to 2.3 s. Results are the same, except that an episode re-read in the last five minutes can count as it was before until the next reload.
An episode’s include accepts the stream’s relations again
Since include=speaker_statement_characteristics was added to GET /v1/podcasts/episodes/{id}, a request that also named a stream or feed relation, such as include=segments,clips,entities, failed with a 422. The endpoint accepts transcript, segments, clips, entities, ads and all again and ignores them, as it did before. It does not embed those relations: read them from the episode’s /transcript, /segments, /clips, /entities and /ads endpoints. An unknown value is still refused.
How a company’s people speak, over time
GET /v1/companies/{id}/statement-characteristics shows how a company’s people speak on podcasts, together, over a window (the last year by default) and per week or month. For each axis (sentiment, certainty, specificity, temporal orientation) it gives every label’s share of their scored lines, with scored_lines, people (how many of them spoke) and low_sample, for the whole window and for each bucket. examples=1–3 adds each bucket’s strongest lines per outer label, each naming its speaker. There are none by default, because for the largest companies they take about twice as long as the counts. People in a current role count by default, and current_only=false adds past roles. The response has the same shape as GET /v1/people/{id}/statement-characteristics.
A person’s statement characteristics leave out doubted lines
GET /v1/people/{id}/statement-characteristics now leaves out lines whose appearance read doubts the speaker is the person, as share of voice and the guest_ filters already do. A person whose linked profile is shared with a namesake may see fewer scored lines than before. The lines left are the ones we’re confident are theirs.
Share-of-voice examples come from more of the conversation
/v1/podcasts/mentions/share-of-voice examples now show at most one line per episode, and lines from different speakers unless too few spoke to fill them. Counts are unchanged.
Statement-characteristics searches by relevance return sooner
An episode search withspeaker_statement_characteristics now reads the lines nearest the query first, and reads deeper only when the page needs more segments. In our checks a warm first page returned in about 1.1 s instead of 1.8 s. Results are unchanged. Searches with sort=recency read as before.
role=executive keeps a company’s officers
GET /v1/companies/{id}/people with role=executive now also returns officers whose title names a function: a Chief Marketing Officer, Chief Revenue Officer, Chief Communications Officer or President of Marketing is among the executives, and still under role=marketing, role=growth or role=communications as before. A Chief of Staff or a Vice President of a function stays in that function’s class only. A title written only as initials, such as CISO, CIO or CHRO, now reads as executive instead of other, in this filter and in sponsor contacts’ role; other titles keep their class.
Alerts on how an entity is talked about
AnENTITY_MENTION alert’s filters take speaker_statement_characteristics, so an alert fires only on mentions said a certain way: {"sentiment": "negative"} for negative mentions of your company, {"specificity": "specific", "temporal_orientation": "future"} for predictions about it, {"certainty": "confident"} for confident claims. Only the mention lines the filter accepts are marked is_mention and counted in mention_count, and previews and the creation backfill apply it the same way. Each scored line of a new match’s windows now carries its speaker_statement_characteristics, filtered or not. The filter returns 422 on PODCAST_SPEAKER and KEYWORD_MENTION alerts. See Alert on how an entity is talked about.
Episode profiles read older transcripts’ speaker roles
In an episode profile, highlight lines now give each speaker’srole as /transcript does: a host that older transcripts spell CO_HOST reads HOST. When two identified speakers share a name, a line goes to the one whose role it carries in that canonical form, so a host’s CO_HOST lines now count in the host’s row instead of the first namesake’s.
Speaker statement characteristics refuse what they used to ignore
On episode search, mentions, the mention timeseries, share of voice and share of voice compare:- A flat axis key such as
sentiment=positivenow returns422namingspeaker_statement_characteristics[sentiment]=positive. Before, it was ignored and the results came back unfiltered. - A repeated key,
speaker_statement_characteristics[sentiment]=positive&speaker_statement_characteristics[sentiment]=negative, now accepts either label, as the comma form does. Before, it kept one. - An unknown axis now returns a
422listing every axis with its labels, instead of a generic “unexpected property”. - On episode search, every
speaker_statement_characteristicsrefusal carries aresolvewith the fix and a link to the right docs section. A text-less search scoped by an entity points at/v1/podcasts/mentions. - The
guest_filters’ “too many guests”422now says how many guests and appearances they select, and itsresolvenames narrower filters, including up to three of a group’s own occupations (e.g.29-1212 Cardiologistsforguest_occupation=physicians).
How an episode was said
GET /v1/podcasts/episodes/{id} takes include=speaker_statement_characteristics for an episode profile: the share of its scored lines carrying each label on each axis, the same per identified speaker, the speaker who carries each pole label most (standouts: the most hedged, the most forward-looking), and up to 5 of the strongest lines for each of predictions, confident_claims, criticism and optimism, with their moment links. Shares are of scored_lines, and low_sample marks fewer than 20. See Episode profiles.
How a show talks
GET /v1/podcasts/{id} takes include=speaker_statement_characteristics: the show’s scored lines over the last 365 days as characteristics, the same breakdown keyed by axis as a person’s, with scored_lines, low_sample and characteristics_version. Beside it, percentiles gives each pole label the share of profiled shows with a lower share: a future percentile of 82 is more forward-looking than 82% of shows. A show without a profile yet returns status: not_computed. See A show’s profile.
Compare how subjects are spoken of
/v1/podcasts/mentions/share-of-voice/compare sets two to five entities’ or companies’ share of voice by one speaker statement characteristics axis side by side: company_id=nvidia,amd,intel&by=sentiment is how each is spoken of, over the same window, speakers and filter. Each subject is exactly what /v1/podcasts/mentions/share-of-voice returns for it with by set to the axis, in the order requested. A subject that matches nothing, or a company with no linked entity, returns 422 naming it.
How a subject is spoken of, over time
/v1/podcasts/mentions/timeseries reads speaker statement characteristics. breakdown=sentiment (or any axis) splits each bucket’s mentions by label, with the bucket’s scored_mentions, a share per label, and low_sample below 20, so “is NVIDIA spoken of more negatively this quarter?” is one call. speaker_statement_characteristics counts only the mentions said a certain way, such as predictions. Either needs since, at most 365 days before until. All new fields are additive; a series without them is unchanged.
The strongest mentions first
/v1/podcasts/mentions takes sort=strength with speaker_statement_characteristics. It returns the episodes whose mentions carry the requested labels most clearly first: the most clearly negative takes on NVIDIA, or the most confident predictions about Ozempic. An episode ranks by its strongest mention, and its windows come strongest first rather than in transcript order. Only mentions that carry labels rank. sort=strength without the filter returns 422, and so does a cursor from another sort.
What was said, beside each share
/v1/podcasts/mentions/share-of-voice takes examples (0–3): each group, or with breakdown each of its labels, carries that many example lines, strongest first, with the speaker, the episode, start_seconds, a moment url and the line’s speaker_statement_characteristics. Additive; without examples the response is unchanged.
Statement-characteristics searches across languages find the best matches
An episode search withspeaker_statement_characteristics and a language other than English now finds the lines nearest your query among all of that language’s podcasts. This matters most when the query is in a different language from the filter: semantic_search=the stock market outlook&language=zh&speaker_statement_characteristics[certainty]=confident returned an empty page before and now returns the matching segments, and English queries over Spanish, German or French podcasts no longer run out of results after the first page. Searches written in the filter’s own language return the same first page as before. These searches can take a second or two longer. English searches, and searches narrowed to one podcast or episode, are unchanged.
Guest-filtered episode searches page faster
An episode search withguest_ filters such as guest_occupation now resolves the guests once per search and reuses them for five minutes, so its later pages no longer repeat that lookup (up to about 3 s on an uncommon occupation). Results are unchanged. A guest listed during those five minutes joins the search once the reuse expires.
The strongest lines, without a topic
/v1/podcasts/episodes/search takes speaker_statement_characteristics with no semantic_search or keyword_search when it is scoped by episode_id, speaker or a guest_ filter. It returns the strongest lines said that way, at most 50, strongest first: an episode’s boldest predictions, or a person’s most confident claims. Those matches have match.source: "characteristics" and no relevance_score. See Without a topic.
How a subject is spoken of, counted
/v1/podcasts/mentions/share-of-voice reads speaker statement characteristics. breakdown=sentiment splits each occupation’s mentions by their sentiment labels, by=sentiment (or any axis) groups the mentions by label instead of occupation, and speaker_statement_characteristics counts only the mentions said a certain way. Label shares are of the new scored_mentions, and a group on fewer than 20 is low_sample. All new fields are additive; a split without them is unchanged.
What experts said, and how they said it
speaker_statement_characteristics now combines with speaker and the guest_ filters on /v1/podcasts/episodes/search, and with the guest_ filters on /v1/podcasts/mentions. Only the lines the selected people say count: guest_occupation=cardiologists with speaker_statement_characteristics[certainty]=confident&speaker_statement_characteristics[temporal_orientation]=future finds confident predictions by cardiologists. On search, the guest_ filters must name an occupation, field or capacity and may select up to 5,000 guests with 50,000 appearances between them, and when more than 10,000 of the people’s lines carry the labels, the newest 10,000 are searched. See Filtering by characteristics.
Filter by how each line is said
/v1/podcasts/episodes/search and /v1/podcasts/mentions take speaker_statement_characteristics as a filter: speaker_statement_characteristics[certainty]=confident&speaker_statement_characteristics[temporal_orientation]=future keeps confident predictions. Labels on one axis are comma-separated and match any of them; axes combine with AND. Every is_match and is_mention line on a filtered page carries labels the filter accepts. Search needs semantic_search with the filter, mentions need a subject with a knowledge graph entity, and neither combines it with speaker or the guest_ filters yet. See Filtering by characteristics.
How each line is said
Lines in the windows of/v1/podcasts/episodes/search and /v1/podcasts/mentions now carry speaker_statement_characteristics, four labels for how the line is said: sentiment (negative, neutral, positive), certainty (hedged, neutral, confident), specificity (vague, neutral, specific) and temporal_orientation (past, present, future). A pole label means the line clearly reads that way, and a borderline line takes the middle label. Lines outside topic-discussion and interview segments, and short lines, carry none. See Speaker statement characteristics.
A company’s sponsor is the one carrying its ads
GET /v1/podcasts/advertising/sponsors/{id} called with a company’s domain or ID now returns the company’s primary sponsor: the one with the most ads naming the company. shopify.com returns Shopify. Before, it returned the most recently seen sponsor with any ad linked to the company, such as Toyota. The sponsor’s /podcasts and /segments follow the same choice.
Company-scoped sponsor lists now include only the company’s own sponsors. This applies to GET /v1/podcasts/advertising/sponsors?company_id=, the sponsor leaderboard with company_id, and GET /v1/podcasts/{id}/advertising/sponsors?company_id=. A sponsor counts as the company’s own when it resolves to the company, or when at least 5 of its ads name the company and those ads are at least 10% of all its ads. A brand with a stray ad or two linked to the company no longer appears. A company’s advertising profile and its ad timeseries still count every ad that names the company.
No sponsor’s last_seen_at is in the future any more, so future-dated sponsors no longer sort first on GET /v1/podcasts/advertising/sponsors.
Chart debuts and re-entries
GET /v1/podcasts/rankings/movers takes change=debut for shows on the chart slot for the first time since tracking began (May 2026), and change=reentry for shows returning to it; new still returns both. Entries keep change: "new" and gain debut, first_charted_at (their first capture on the slot), and, on debuts whose whole back catalog is indexed, first_episode_published_at. GET /v1/podcasts/{id}/rankings/history with country set is now fast for popular shows: a three-month US history returns in milliseconds.
A guest’s pitch list leaves out shows not worth pitching
GET /v1/podcasts/guests/{id}/recommendations/podcasts now screens its list:
- It leaves out shows the person already appeared on, including under another record of the same identity, and other feeds of those shows with the same publisher and host.
- It leaves out shows that have stopped publishing, and shows rated
UNSAFEunless you passinclude_unsafe=true. - Shows charting far less widely than the person’s usual venues rank lower.
suitability_tier, last_episode_at and chart_count.
Episodes without a publish date are no longer dated 1970
Some feeds give an episode no usable publish date, or a bare year that used to be read as a moment on January 1, 1970. Such a back-catalogue episode now has nopublished_at, and a show’s coverage no longer reports 1970 as its oldest episode: the episode counts in the totals but in no year. In an episode listing it comes after every dated episode. Its transcript_status is unavailable, and a transcription request for it answers 422 episode_audio_unavailable, since an episode we cannot date cannot be filed by day in your archive or listings.
Transcription requests are charged in USD
A transcription request that queues an episode is now charged in USD at your plan’s transcription price, and no longer draws 100 of your plan’s included requests. It is charged even when you have requests left, and it keeps working after they run out. Because only a USD charge can pay for it, it needs overage billing turned on and a payment method on file (Enterprise contracts are invoiced and need none); otherwise it answers402 overage_required or 402 billing_info_required, and nothing is queued or billed. Repeat requests answered 200 are still billed at the lookup price. Since October 30 it costs $0.05 per episode on every plan.
Request a podcast we don’t index
POST /v1/podcasts/requests asks us to index a show that isn’t in the catalog: send its Apple Podcasts, Spotify or YouTube link, or its RSS feed URL. Requests are free. Within a minute or two, automatic screening adds an English-language show from Apple Podcasts or an RSS feed to the catalog. Shows that screening can’t decide, including Spotify and YouTube links and shows not in English, go to our team. GET /v1/podcasts/requests/{id} returns the outcome: state is requested, indexed (with the podcast), in_review or rejected, with a reason code and a detail sentence. GET /v1/podcasts/requests lists your organization’s requests. Each organization can request 50 new shows in any 24 hours; past that, the request returns 429 rate_limit_exceeded with a Retry-After header and the time you can request more.
The guests behind a show’s expertise profile
GET /v1/podcasts/{id}/guests/expertise/{code} lists the guests behind one occupation or field of research on a show, most appearances first, such as GET /v1/podcasts/all-in/guests/expertise/political-science. It takes any occupation or field at any level, including SOC groups and everyday names such as doctors, and counts what the show’s profile counts, so the guests’ appearances add up to the profile’s count for the code.
Lists reject bad cursors and filters that match nothing
Three changes make an empty or repeated page mean what it says. They can change what an existing integration sees, so check the first two before you upgrade a client:- A cursor no response issued is rejected. A truncated, re-encoded, or made-up
cursorreturns422 validation_erroratquery.cursor. Before, it was read as the first page, so a client that mangled its cursor fetched page one forever. - A filter that matches nothing is rejected.
podcast_id,episode_id,company_id,entity_id,publisher_id,topic_id, andtickerfilters on lists and timeseries return422 unresolved_referencewith the endpoint that resolves the value, as search already did. Before, they returned an empty200. A filter that takes several values (tickeronGET /v1/stories,entity_idonGET /v1/companies) is rejected when any one value matches nothing, naming that value; before, the unmatched values were dropped and the request filtered on the rest. This includes the subject and filters ofGET /v1/podcasts/mentions/by-podcast, which returned an empty200for an unknown subject when it launched. A filter that matches something with no results still returns an empty page, andperson_idis unchanged. The same rule now coversGET /v1/podcasts/mentions/share-of-voiceandGET /v1/companies/{id}/podcast/coverage. On share of voice, anentity_idorcompany_idsubject, apodcast_id, or apublisher_idthat matches nothing returns422 unresolved_reference; before, it returned an empty split. On company coverage, apublisher_idthat matches nothing returns the same422; before, it returned an empty matrix. A company with no linked entity is still a valid share-of-voice subject and returns an empty split. OnGET /v1/stories, an unmatchedtopic_idis now rejected even beside atickerorcompany_idthat has no linked entity. datais always an array. An empty list is{"data": []}, nevernull, and the OpenAPI document no longer allowsnullthere.
GET /v1/podcasts/rankings/movers now pages: it sets has_more and cursor, and takes cursor. The OpenAPI document also marks every operation with x-pagination (cursor or none).
Usage counts what each call draws
GET /v1/me/usage now counts billable_requests in the requests a call draws from your plan, not one per call. A transcription request that queues an episode draws 100 requests on plans billed in requests, so it now counts 100, matching what your plan’s included requests (or overage) are charged. Other calls count what their price draws, which today is 1 for every other endpoint and tool and for a transcription request answered 200. A call to an endpoint or tool your subscription does not price, and a refunded transcription request, no longer count. The new billable_basis says how the count was made: requests on plans billed in requests, calls on plans billed in dollars, where each billed call counts once, and calls_fallback when billing data could not be reached.
Podcast external links list only the show’s own verified presences
GET /v1/podcasts/{id}/external-links no longer lists LinkedIn for a podcast. LinkedIn identifies people and companies, and the links stored for shows were mostly a host’s or a guest’s profile; a host’s LinkedIn is on GET /v1/people/{slug}/external-links. Links whose identifier was a placeholder such as not_found or N/A are no longer returned on any platform, and handles that carried invisible characters now come back with a working URL.
Failed transcription requests are refunded
A billed transcription request that endsfailed is now refunded automatically, usually within half an hour: the included request, prepaid credit or overage it used is returned. The request gains refunded_at, set when it is.
Faster transcription requests, with no limit on how many
Transcription requests are transcribed concurrently, episodes of one podcast included, and most finish within a few minutes. There is no limit on how many you send. The guide now lists how long to expect by episode length.Every time window is since and until
Endpoints that spelled their time window published_after and published_before now take since and until, like the rest of the API: the episode lists and timeseries, a podcast’s episodes and mentions, mention timeseries and share of voice, advertising timeseries, and guest appearances. Alert matches take since and until in place of after and before. Before, since on these endpoints was ignored, and the response came back unfiltered.
The old spellings still work and are marked deprecated in the API reference. A request that sends both spellings with different values returns 422, and errors[].location names the deprecated one. The publisher bias leaderboard’s since and until now accept a bare date, and a bare until covers that whole day. Time windows lists every window parameter and what it bounds.
Error details name parameters in errors and operations in resolve
Validation errors no longer put API paths or parameter names in detail. A missing, conflicting or unknown parameter is listed in errors[].location, such as query.entity_id, and the operation that lists its values or does what you meant is in resolve.endpoint. This applies to the mention, share-of-voice, advertising timeseries, segment and expert-ranking requirements, unknown entity types, entity charts and chart categories, and requests sent to the wrong search. The status codes and error_code values are unchanged.
Search the lines a person said
GET /v1/podcasts/episodes/search takes speaker, a person slug or ID. It keeps only segments in which that person speaks a line, and only their lines are marked as matches, so keyword_search="irrelevant marketing term"&speaker=sam-altman returns the interview where he said it. With entity_id, a host quoting him counts as a match too. With keyword_search, the person’s own lines must carry the query; a quote the transcript splits across their consecutive lines counts, and is marked whole. A clip of them played on another show is not them speaking, so the original turns up and the replays do not.
Transcript ranges, speaker filters, and the word transcript’s language
startandendon the episode transcript and the word transcript return every line or word that overlaps the range. Before, they returned only lines wholly inside it, so a short range inside a long line came back empty.- The transcript’s
speakertakes person slugs and IDs, as the speakers endpoint returns them. entity_idonGET /v1/podcasts/episodes/searchand the episode lists takes a person with no linked entity, matching the episodes they speak in. Before, it returned422 unresolved_reference. A slug that names both an entity and a person means the entity; pass the person’s ID to filter by the person. Such a person passed together withcompany_idnow returns422, where before the request quietly answered for the company.- The word transcript’s
languageis the episode’s language tag (en,en-US), the same value the dialogue transcript reports. It sometimes returned the speech-to-text provider’s three-letter code (eng) instead.
Company search ranks exact name matches first
GET /v1/companies?q= now returns exact matches with news coverage before other names that contain the query. An exact match is a company whose name is the query once case, spacing, punctuation, a leading “The” and corporate suffixes are set aside, or one with an alternate name equal to the query. ?q=Mercury now returns Mercury, the fintech company, first; before, Bluemercury and Mercury Records ranked ahead of it. ?q=athletic now returns The Athletic first. Within exact matches, and among the other results, companies are still ranked by news coverage. Several companies can share a name, so check the domain or description before picking one.
Faster unfiltered company and clip lists
GET /v1/companies with no search or filter now returns the companies with the most news coverage first, in a fraction of a second. It used to take 10–40 seconds and return the most recently updated companies. The unfiltered list holds only companies with news coverage. Filtered lists rank the same way and still return every match, with uncovered companies last. GET /v1/podcasts/clips without filters, or filtered only by type or min_engagement, now reads clips in engagement order instead of sorting the whole catalogue first, so it no longer times out.
Moment links on every hit
Search matches and their windows, mention windows, clips, segments, ad placements and entity chart receipts carry aurl that plays the moment on Radar: https://radar.particle.pro/podcasts/<podcast>/episodes/<episode>?t=<seconds>. Cite it and the reader lands on the exact quote. A chart narrative’s quote has quote_url. The embedded episode on these responses gains web_url, the episode’s page, to which you can append ?t= for any other offset. See Moment links.
Podcast and episode {id} slots now also take a raw UUID, so a returned link resolves when passed back as an ID.
Find topics by name
GET /v1/topics takes q. It returns topics whose names match: exact names first, then names starting with q, then names containing it, each ranked by story coverage. With parent_id or ancestry_path_prefix, it searches that topic’s descendants. Name search covers topics with at least 5 story clusters. Before, an unknown q was ignored and the root topics came back.
Entities by podcast cover every show
GET /v1/entities?podcast_id= returned an empty list for most podcasts. It now covers every show with transcribed episodes, ranking entities by how many of the show’s latest 500 episodes mention them. Mentions inside ad reads and the show’s own hosts are left out.
Name search finds names written as one word, and ranks names above nicknames
InGET /v1/entities/search and GET /v1/companies?q=, a query written as separate words now finds a name written as one: open ai finds OpenAI, which it missed before. GET /v1/companies?q= also lets any single character stand between words (coca cola finds Coca-Cola), matches a company’s known aliases, and treats % and _ in q as literal characters rather than wildcards.
In entity search, a name now ranks ahead of a nickname of similar popularity: for Mercury, the planet ranks above the WNBA’s Phoenix Mercury, which matches only by alias. Aliases of three characters or fewer, and all-caps codes that several entities share, such as SAM (Samoa, the System for Award Management, a dozen others), now report match_quality: strong instead of exact, so sam ranks Sam Altman first. When several results are exact, they are different things with the same name: pick the top result automatically only when it is the only exact one.
Particle CLI examples across the docs
Every endpoint in the API reference now shows the equivalent Particle CLI command beside its curl request: pick Particle CLI in the request example’s language menu. The guides do the same, with a CLI tab next to each curl example. Each command is the one a CLI user would type, makes the same request as the curl beside it, and comes from the CLI’s own command list. Install it from cli.particle.pro. The request examples for creating and previewing an alert, updating an alert, and creating a webhook connection now send a complete body instead of{}.
Facts on chart entries
Entries onGET /v1/podcasts/entities/charts/{category} gain facts: the network of a TV series, the developer of a video game or software application, and the publisher of a video game, book, or periodical. Each fact links to the company’s own entity when it has one. An entity carries at most one fact of each kind, and facts is absent for every other entity.
Response fields that save a lookup
- Companies carry a
slugonGET /v1/companies/{id}andGET /v1/companies, the handle compact companies already carried. - On
GET /v1/podcasts/guests/{id}/appearances, eachepisodecarries itspodcast, as search and mention hits do. The top-levelpodcaststays. - Transcription requests name their
episode(id, title, publication date, and slug once transcribed) and theirpodcast, onGET /v1/podcasts/transcription/requests, its single-request form, and thePOST. GET /v1/podcasts/guests/trendsnow fillsguest.first_appearance_at, the guest’s first appearance anywhere in the corpus.
segment.episode and clips[].episode on episode search matches, where the match’s own episode is theirs, and the edition-level caveats on entity charts, where caveats are per entry. None of them ever held a value, so no response changes.
See what a key has used
GET /v1/me/usage returns the requests an API key made in the current billing period, or with period=today the current UTC day, broken down by endpoint and MCP tool, with how many bill per call. It is free.
Timeouts and unresolved filters return their own errors
A request that runs past its time limit now returns504 request_timeout on every endpoint and MCP tool. Some endpoints used to return 500 internal_error instead. Narrow the request and retry; a 500 again means the server failed.
A filter value that matches nothing now returns 422 unresolved_reference with the call that resolves it, as search already did. This replaces a 404 on entity_id and company_id for /v1/podcasts/{id}/mentions, company_id for /v1/podcasts/advertising/timeseries, compare_with for /v1/companies/{id}/podcast/coverage, entity_id for /v1/podcasts/episodes/{id}/transcript/mentions, and podcast_id for GET /v1/podcasts/transcription/requests.
On /v1/podcasts/{id}/mentions, entity_id and company_id are checked separately, and both apply. If either one doesn’t match, the request returns 422; before, the response quietly used whichever filter matched. If the company matches but has no linked entity, the response is an empty page, as in search. Before, a valid entity_id sent alongside such a company returned that entity’s mentions.
Discovery documents are compressed and revalidate
/openapi.json, /llms.txt, and /agents.md are sent gzip-compressed to clients that accept it (the OpenAPI document drops from 1.8 MB to about 150 KB) and carry a strong ETag. Send it back as If-None-Match and an unchanged document answers 304 Not Modified with no body.
The quickstart, agent onboarding, and the api_key_required page now cover the Particle CLI: particle auth login signs in through the browser and saves a key for the CLI.
A short semantic search on one show says when keyword_search finds it
A one-wordsemantic_search scoped to a show, such as semantic_search=tariffs&podcast_id=all-in, can return nothing while the show says the word many times. A short query matches long passages weakly. The empty-search diagnostics now report this as the new outcome weak_semantic_match, with a verified top-level retry_with that adds the words as keyword_search and keeps your filters. The podcast_id and episode_id remedies no longer say the show or episode has no matching dialogue when only the relevance cutoff was missed.
Fixed-value parameters accept any case and reject unknown values
A parameter that takes a fixed set of values now accepts them in any case:type=ad on GET /v1/podcasts/segments is type=AD, and bias=left is bias=LEFT. A value outside the set returns 422 naming the allowed values, where some parameters used to return an empty list. The OpenAPI spec now lists the allowed values as an enum for segment and clip type, product status, and publisher bias.
GET /v1/podcasts/rankings and its movers answer 422 for a country or category_slug with no current chart, offering the closest category slugs, instead of an empty page. country=uk is accepted for gb. Entity chart slugs in the path, such as /v1/podcasts/entities/charts/Movies, also ignore case.
Retry writes safely with Idempotency-Key
The createPOSTs take an optional Idempotency-Key header: alerts, webhook connections and their secret rotation, test webhooks, and transcription requests. Send one key per write and resend it on every retry. The write runs at most once, and a retry gets the first response back with Idempotent-Replayed: true, unbilled, for 24 hours. The same key with a different request returns 422 idempotency_key_reused, and a retry while the first attempt is still running returns 409 idempotency_key_in_use. These operations carry x-idempotent: true in the OpenAPI document. Requests without the header behave as before.
Check which key you’re using
GET /v1/me returns the organization, project, and plan an API key belongs to, with the key’s name and prefix. It is free: call it to confirm a key works and which project it reports to.
Look up shows and episodes by link, and Spotify episodes
GET /v1/podcasts/lookup and GET /v1/podcasts/episodes/lookup take links as they are: paste a show’s or episode’s Spotify, Apple Podcasts or YouTube URL, a show page on another platform, or a feed URL, and it is resolved by what it points to. platform is now optional when every identifier is a link, and links from different platforms can share a call.
GET /v1/podcasts/episodes/lookup also resolves Spotify episodes: Spotify episode IDs with platform=spotify, episode URLs and spotify:episode: URIs.
Share of voice: coverage of the counted speakers, and each group’s top level
GET /v1/podcasts/mentions/share-of-voice adds speakers, speaker_mentions (the lines of the speakers counted, the denominator for coverage) and host_mentions. At level=detailed each group carries top, the SOC major group or ANZSRC division it sits under, with that group’s share. Defaults and existing fields are unchanged.
Share of voice for a keyword
GET /v1/podcasts/mentions/share-of-voice takes keyword_search in place of entity_id or company_id. It splits the lines that say a word or quoted phrase by the occupations or fields of the people who said them, counting a line only when its own speaker said every word. The response carries keyword instead of entity. A keyword’s window may span at most 90 days, and one said in more than 3,000 episodes in the window is refused with ways to narrow it. Passing both a keyword and an entity returns 422.
Transcription requests
Request the transcript of anyrequestable episode in a podcast’s back catalogue with POST /v1/podcasts/transcription/requests. A request starts transcribing within seconds while there is capacity, and the episode keeps its ID, so once it reads transcript_status: transcribed every episode endpoint serves it. Only a request that queues an episode is billed as a transcription; requesting one again, or one another organization already queued, is billed as a lookup. GET /v1/podcasts/transcription/requests lists your requests and is not billed. transcript_not_available now points at the request endpoint in its resolve.
Expertise beside every speaker
Wherever a response names a speaker or a guest, it now says what they know. Each person carries a compactexpertise: standing, seniority, and up to three of the most specific SOC 2018 occupations and ANZSRC 2020 fields, with the codes and slugs guest_occupation and guest_field take.
- Transcript search matches and mention episodes gain
speakers: the identified people speaking in their lines, matched to each line by name. - Episode speakers on
GET /v1/podcasts/episodes/{id}and/speakerscarryexpertise. - So do the guest directory, a show’s roster and guest trends.
expertise is left out when the person has no profile yet, when their link is unsettled, when that appearance may describe someone else, or for a role profiles don’t cover, such as a moderator. See beside every speaker. Responses otherwise are unchanged.
Occupation groups and everyday names
Everyoccupation and guest_occupation parameter on the expertise endpoints and filters now takes SOC 2018 minor and broad groups as well as detailed occupations, so physicians (29-1210) covers all 13 physician specialties and 29-1000 covers 45 healthcare practitioner occupations. Everyday names such as doctors, medical-professionals, nurses, professors, journalists and athletes stand for the occupations people mean by them. GET /v1/people/occupations browses through the groups, and a group comes back with a new group field.
Changed: GET /v1/people/occupations?parent= a SOC major group, such as 29-0000, now lists that group’s minor groups rather than its detailed occupations, and each minor group lists its broad groups. To reach the detailed occupations, keep browsing with parent, or pass the group itself to an occupation filter, which matches everything under it.
Words that name a field or a capacity, and guest_capacity
Words with no useful SOC 2018 occupation now filter by the field of research or capacity they name.guest_occupation=oncologists keeps guests in field 3211 Oncology, investors and bankers reach the finance fields, and founders, politicians, pundits and influencers reach the capacities executive_founder, public_official, journalist_analyst and creator_entertainer. GET /v1/people/fields?q= finds a field by these words too. A new guest_capacity filter on GET /v1/podcasts/episodes, its timeseries, transcript search and both mention endpoints keeps guests who know their field in that capacity; on the timeseries it narrows a subject rather than being one. The podcast list accepts a word for a field but rejects one for a capacity, since show profiles count guests by code.
Current team for NFL, MLB and NHL players
Thesports object on GET /v1/people/{id} and GET /v1/podcasts/guests/{id} carries current_team for NFL, MLB and NHL players: the team the league’s current roster lists them on, with its entity_slug, the league, and as_of, when the roster was read. Rosters are read daily, so a trade shows up the next day. A player who is not on one of those rosters, including anyone in another league, has no current_team, and neither does an NHL player who has changed teams during the season, since the NHL feed does not say which team is current; a team in teams with no end_year is not proof the player is still there.
Speaker expertise
Podcast hosts, guests, panelists and correspondents now have expertise profiles: their occupations (SOC 2018, with ISCO-08 equivalents) and fields of research (ANZSRC 2020), how established their standing is, how senior they are, whether they still practice, and the credential that establishes it, quoted from the public record. Each guest appearance is read too: whether the guest spoke within their field, whether the appearance was mainly promotional, and how the show introduced them.- People:
GET /v1/people/expertisefinds the people listed under an occupation or a field, andGET /v1/people/{id}/expertisereturns one person’s profile.GET /v1/people/occupationsandGET /v1/people/fields, each with a/{code}variant, list the codes you can filter by. - Who is being booked:
GET /v1/people/occupations/{code}/guestsandGET /v1/people/fields/{code}/guestsrank the most-booked guests in an occupation or field; the matching/podcastsendpoints rank the shows that book them. - Episodes and shows:
GET /v1/podcasts/episodes/{id}/expertiselists an episode’s guests with their profiles and how each appearance reads.GET /v1/podcasts/{id}/guests/expertiseprofiles a show’s guests, and the podcast list filters by it withguest_occupation,guest_field,min_established_guest_share,min_in_field_guest_share,max_promotional_guest_shareandmin_analyzed_guest_appearances. - Filters: the episode list, transcript search, mentions and their timeseries take
guest_occupation,guest_field,guest_standing,guest_seniority,guest_practicingandguest_in_field. The guest directory takesoccupation,field,standing,seniority,practicing,capacityandemployer, and a guest’s appearances takein_fieldandexclude_promotional. - Share of voice:
GET /v1/podcasts/mentions/share-of-voicesplits the lines that mention a company, person or topic by the occupations or fields of the people who said them.
A person’s current role prefers a titled one
When a person holds several current roles,current_role on GET /v1/people/{id} and the guest endpoints, and the title and company on an episode entity’s entity.person, now come from a titled role before an untitled one, then from the most recent start. Untitled company affiliations used to take precedence over a titled executive role because they were recorded more recently, so a chief executive could read as an untitled affiliation with no title. The episode stream, the feed and the episode archive now name the same role as GET /v1/podcasts/episodes/{id}/entities for every person.
Sports history on people
GET /v1/people/{id} and GET /v1/podcasts/guests/{id} return a sports object for athletes, coaches and other sports figures: the sports they play, the teams they played for or coached (role is athlete or coach, with start_year, end_year, and the leagues the team plays in today), and leagues recorded on the person directly. Each team and league carries an entity_slug for GET /v1/entities/{id}. A missing end_year means no end is recorded, which does not by itself mean the person is still on the team. People with no sports history are unchanged.
Each segment listed once
GET /v1/podcasts/episodes/{id}/segments, GET /v1/podcasts/segments and GET /v1/podcasts/segments/{id} no longer repeat an ad segment once for every ad read in its break. The repeats could push an episode’s last segments out of its segment list, which now returns every segment. An ad segment’s read_type is now always its first ad read’s. Paging through GET /v1/podcasts/segments no longer repeats or skips segments created at the same moment, such as an episode’s segments, which are all written at once.
Eight more sports charts
GET /v1/podcasts/entities/charts lists eight more sports charts: wnba, college-basketball, formula-1, nascar, and pro-wrestling for one league each, and soccer, golf, and tennis for a whole sport. They rank the way the league charts do; see League chart entries. On golf and tennis, the majors and Grand Slams chart alongside the players. The new charts publish 7d only, and all-sports now pools all thirteen sports charts. all-sports also applies the league charts’ top-10 rules now, so a league organization or a member the league shares with a neighbor sits directly below its top 10, and it measures reach against each league and the whole pool together.
Zero data retention
Zero data retention is available per organization on request: Particle keeps that each API and MCP request happened (time, endpoint, status, duration, size, organization, project, and credential) but not its contents. Organization settings in the platform dashboard shows whether it is on and how to request it. Alerts and Radar are not covered.Speaker names follow the show’s own spelling
A speaker’s name is now spelled the way the show’s own title or notes print it, rather than the way speech-to-text heard it: a host who introduces herself as “Kristen Plake”, transcribed “Kristen Plaik”, is labelledKristen Plake wherever the podcast’s description says so. The spelling is the same on every transcript line’s speaker, in the line text where the name is spoken, on word-level transcripts, in an episode’s speakers, and on the linked person. When a person’s spelling is corrected, their slug changes to match (kristen-plake); the old slug keeps resolving to the same person, so stored links and integrations keep working. Episodes transcribed earlier are corrected too, across the same fields, once a later episode or the show’s notes settle the spelling.
People and companies on episode entities
Entities embedded in episodes by the episode stream, the episode feed and the episode archive now carryentity.person or entity.company when the entity is a known person or company, exactly as GET /v1/podcasts/episodes/{id}/entities returns them, so a mention joins to the person or company it names by id.
Show archive
GET /v1/podcasts/archive/download returns every podcast we cover as one gzip-compressed JSON Lines file: each show exactly as GET /v1/podcasts/{id} returns it with every section, plus its external links with their attributes. It is rebuilt twice a day, answers 304 to an unchanged If-None-Match, and comes with the episode archive.
Steadier episode payloads and fuller mention lines
Episodes delivered by the episode stream, the episode feed and the episode archive no longer carry the podcast’spopularity on the podcast inside their segments’ and clips’ episode references. Popularity is re-ranked daily, so a stored copy goes stale overnight; read it from GET /v1/podcasts/{id} when you need it. Other endpoints are unchanged.
An entity’s mention_lines now also lists the lines that use the entity’s own name when extraction recognized it under another form: an entity “Pope” annotated as “pontiff” now includes the line that says “the Pope”.
Entities and ads in the episode stream
The episode stream and the episode feed embedentities and ads at the fully_ingested milestone, by name or with include=all, in the same shape as episode archive records. fully_ingested now waits for entity extraction and ad analysis as well as clips, so the episodes it delivers carry all three; this usually adds under a minute.
Entities and ads in the episode archive
Episode archive records carry two more relations:entities, the knowledge graph entities discussed in the episode, each with its salience, its number of mentions and the transcript lines that mention it; and ads, the advertising spots detected, in playback order. Narrow them away with include like the others. They describe the episode itself; data that changes over time, such as an entity’s linked person or a sponsor company’s profile, is left to the entity and company endpoints.
Episode archive
The episode archive delivers every transcribed episode we hold as gzip-compressed JSON Lines files, one per month or day of publication, for loading our full history before following the episode stream. Each line is an episode exactly as the stream sends it withinclude=all. The archive is updated daily: late episodes, imported back catalogues and corrections are folded into the days they belong to, and updated_since on the listing returns only the days that changed, so a copy can be kept current. Episodes can also be replayed over Server-Sent Events. Available by arrangement; organizations without access receive 403 episode_archive_not_enabled.
Ad segments are no longer repeated
Withinclude=segments, the episode stream and the episode feed returned an ad break once per ad read in it, so an episode could list the same segment several times and more segments than its segment_count. Each segment now appears once. When a break holds several ads, its read_type is that of the first ad recorded, where before it varied between requests. Episode topics also come back in a fixed order now.
September 2026
Corpus totals by transcript status
GET /v1/podcasts/stats adds episodes.totals: how many episodes are transcribed, and how many more are requestable in podcasts’ back catalogues, discovered but not transcribed, with totals_counted_at. Coverage from GET /v1/podcasts/{id}?include=coverage now reads from the same hourly counts. It answers instantly for any show, gains counted_at, and reports back_catalogue only once the counts include it.
Untranscribed episodes say so, and queued
A back-catalogue episode’s transcript, segments, speakers, entities, topics, ads, clips and related episodes now answer 409 transcript_not_available instead of 404: the episode exists, but has not been transcribed. One whose audio cannot be fetched answers 422 episode_audio_unavailable, since it never will be. An ID that matches nothing is still 404. transcript_status gains queued, for a back-catalogue episode whose transcript has been requested and is waiting to be transcribed; coverage counts it too, and transcript_status=requestable still lists it.
Coverage: how much of a show’s history is transcribed
GET /v1/podcasts/{id}?include=coverage adds a coverage section: every episode we know of (discovered_episode_count), split by transcript_status and by publication year, plus back_catalogue, which says when the show’s back catalogue was imported and whether it is complete. It is absent until the import has run. See transcript status and the back catalogue.
Transcript status and the back catalogue
Every episode now carriestranscript_status: transcribed, processing, requestable or unavailable (one that can never be transcribed, such as one whose audio is gone). A show’s older episodes, which we discover in its feed but have not transcribed, are its back catalogue. The episode lists leave them out unless you ask: pass podcast_id and transcript_status=any, untranscribed or requestable to GET /v1/podcasts/episodes or GET /v1/podcasts/{id}/episodes; transcript_status=transcribed replaces has_transcript=true. GET /v1/podcasts/episodes/{id} returns a back-catalogue episode by id, and episode lookup now resolves a back-catalogue episode’s Apple Podcasts ID, guid or PodcastIndex ID instead of missing, with has_transcript and transcript_status on every result. Responses without transcript_status are unchanged.
Podcast lookup ignores identifier case and accepts platform=x
GET /v1/podcasts/lookup now matches identifiers regardless of case, so platform=twitter&identifier=thepeelpod finds the podcast whose X handle is ThePeelPod. Each result still echoes the spelling you sent, and an identifier stored in exactly your casing wins over one that differs only by case, so an ID that resolved before resolves to the same podcast now. platform=rss and platform=website keep matching URLs exactly. The platform parameter is case-insensitive too, and x is accepted as an alias for twitter there and on GET /v1/entities/lookup.
Sports and entertainment entity charts
GET /v1/podcasts/entities/charts lists seven more charts. Five league charts, nfl, nba, mlb, nhl, and college-football, rank the players, teams, coaches, and events of one league together, by a blend of reach and 52-day elevation; see League chart entries. Two composites, all-sports and all-entertainment, pool the league charts and the tv-series, movies, and video-games charts, and rank the way all does. Each directory row gains group (overall, entertainment, or sports), for building tabs without a hard-coded chart list, and windows, the windows that chart publishes. The composites publish 7d only.
Keyword alerts
Alerts gain a third kind,KEYWORD_MENTION, that watches a phrase instead of an entity — for a startup whose brand differs from its legal name, a product, a drug, or any name the entity resolvers don’t know. Create one with kind: "KEYWORD_MENTION" and keyword in place of entities, and set description to say what the phrase means so same-name mentions of something else are filtered out. The phrase matches the way a double-quoted phrase in keyword search does, in topic-discussion and interview segments only. Preview, backfill, filters, digests, Slack and webhooks work as they do for entity alerts. A phrase that matched more than 700 podcast episodes in the past week is rejected as too broad on create, edit and preview. entities is now required only for entity kinds; alerts, matches and delivery payloads carry a keyword field on keyword alerts. See Alerts.
Episode search: format=compact for screening
GET /v1/podcasts/episodes/search takes format=compact. The page carries the same ranked matches with windows and clips omitted, and skips the transcript load and line scoring that produce them, so it is a fraction of the size and returns sooner. Each match keeps its episode, its segment (id, title, description, summary, bounds), and its match score; ranking, ordering, has_more, and cursor are unchanged. It is for sweeping many companies, themes, or dates and fetching dialogue only for the matches worth reading. The default format is unchanged; windows is now documented as optional in the schema because the compact page omits it.
On GET /v1/podcasts/mentions, every is_mention line now carries segment_id, the segment containing that line. A window’s segment is where the window opens, context_lines before its first mention, so a mention in a segment’s opening lines was only citable to the previous segment; the line-level id is the citation.
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 gaincompany_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.
Sponsor prospecting: one row per advertiser, venues cited by relatedness, scores that spread
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.
Related shows: a relatedness floor, screened feeds out, duplicate feeds collapsed, and a coverage note
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.
Recommended guests rank by breadth across publishers
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.
Sponsor prospecting: contacts and the long tail; related-show filters
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 band — strong (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/episodesandGET /v1/podcasts/{id}/episodespreviously embedded every speaker, whileGET /v1/podcasts/episodes/{id}returned only conversation participants — the same episode gave two different answers. Both now returnHOST,GUEST,PANELISTandCORRESPONDENT. If you were reading advertisers or soundbite speakers out of a list response, read them fromGET /v1/podcasts/episodes/{id}/speakers?role=…instead. roleaccepts every documented value.soundbite_speaker,narrator,announcer,voice_actor,moderator,audience,producerandotherare now valid filter values; previously several roles appeared in responses but could not be filtered for.role=unknownnow 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=hostand?role=HoStare equivalent, on the REST API, in MCP tool arguments, and in alertspeaker_roles. Responses continue to use uppercase. Alertspeaker_rolespreviously rejected lowercase with a422. CO_HOSTis retired. It was never produced after May 2026 and is now folded intoHOST. Filtering onco_hostis no longer accepted; usehost, 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
testboolean, set totrueonly 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 asdelivered: falsewhile the request itself still returns200.
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.relevance—EVERYTHING(default) returns on-target and incidental matches;RELEVANTnarrows to on-target only, dropping passing mentions.source_popularity—ANY(default) keeps every match;POPULARkeeps only matches from the top 5% of currently-charting podcasts.speaker_roles—PODCAST_SPEAKERalerts only. Replaces the default appearance set (GUEST,PANELIST,CORRESPONDENT,AUDIENCE,SOUNDBITE_SPEAKER—HOSTexcluded). Sending this on anENTITY_MENTIONalert returns422.
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 onGET /v1/entitieshas been removed. Move name search to/v1/entities/search— passingqtoGET /v1/entitiesnow returns a422directing you there.GET /v1/entitiesstill lists the most-mentioned entities and filters bytype,podcast_id, orids. - Search results are union-shaped (a person, company, or knowledge graph entity) rather than the always-an-entity shape
GET /v1/entitiesreturns — 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 toGET /v1/podcasts/episodes/search— the parameters and response shape are identical to what/v1/podcasts/searchreturned 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/searchis now the canonical way to search podcasts, and theqparameter on/v1/podcastsmay be retired in a future version.- Podcast search is metered as a standard endpoint — searching the catalog at
/v1/podcasts/searchnow costs the same as/v1/podcasts.
Missing something? Email api@particle.pro.