Skip to main content
Tool behavior grows additively — new include sections and modes never break an existing call. Renames and changes that could affect your integration are called out explicitly.

September 2026

Pay per request with x402 — keyless POST /mcp

A keyless POST to the MCP endpoint now returns 402 with two challenges instead of a bare 401: the x402 payment requirements in PAYMENT-REQUIRED (a flat $0.01 in USDC on Base per HTTP request) and the usual WWW-Authenticate: Bearer … resource_metadata="…" pointer for OAuth clients. An agent with a funded wallet can pay per request and call any payable read-only tool without an account (project-scoped tools such as the particle_alert_* family still need a credential); a paid request skips the bearer requirement and is served exactly as a keyed one. OAuth and API-key traffic is unchanged — any presented credential takes the normal auth path. See Pay per request with x402. A new standard-tier particle_podcast_list_related_episodes answers “who else covered this”: episodes from other shows on the same story or subject as an episode, best first, each with a score, a band, and — with include: ["basis"] — the shared entities, topics, news story, guests and days apart. published_within_days narrows to a recency window and same_podcast opts the source show in. Not a topic search (particle_podcast_search_transcripts) or a mention list (particle_podcast_find_mentions). The REST counterpart is GET /v1/podcasts/episodes/{id}/related. A new standard-tier particle_podcast_list_related answers “shows like this show”: the shows most related to a podcast, best first, each with a calibrated score, a band (strong / moderate / weak) to branch on, and — with include: ["basis"] — the signals behind the pairing (content similarity of recent episodes, shared topics, named shared guests, same publisher, shared sponsors). Related sets are precomputed per show and restricted to its language; a show whose set has not landed yet returns an empty list rather than an error. particle_podcast_resolve gains include: ["related"] for the five most related shows inline. Slugs are edges as always: every related show feeds the podcast tools, and the people and topics in the basis feed the guest and resolve tools. The REST counterpart is GET /v1/podcasts/{id}/related. particle_podcast_resolve takes include: ["recommended_sponsors"] (premium-grade) to attach the five advertisers a show could pitch: sponsors that run on its related shows but not on it, each with the linked company, a score, a band, how many related shows run it, its ads and most recent ad across them, and which shows. The paged form is GET /v1/podcasts/{id}/recommendations/sponsors. Three of the get/resolve tools gain an inline section, so the common follow-up needs no second call: particle_podcast_get_episode takes include: ["related"] for the five episodes from other shows most related to the one you have; particle_podcast_get_guest takes include: ["recommended_podcasts"] for the five shows a person could plausibly appear on next — shows related to the ones they have guested on, minus those, with the venues behind each pick (the pitch list); and particle_podcast_resolve takes include: ["recommended_guests"] for the five guests a show could book that it hasn’t — people booked by its related shows, with how many and which (the booking pipeline). Each section is present even when empty, with a note saying why, so an honoured-but-empty include is distinguishable from one not requested. The paged REST forms are GET /v1/podcasts/guests/{id}/recommendations/podcasts and GET /v1/podcasts/{id}/recommendations/guests. Include values are now validated on these three tools. An unknown value in include on particle_podcast_resolve, particle_podcast_get_episode or particle_podcast_get_guest returns an invalid_parameter error naming the accepted values, where it was previously ignored. Calls that pass documented values are unaffected.

Segments carry transcript line ranges, and the tiling claim is corrected

Segments now report start_line and end_line — 1-indexed inclusive transcript line numbers — on particle_podcast_get_episode with include: ["segments"], and on GET /v1/podcasts/episodes/{id}/segments, GET /v1/podcasts/segments, and GET /v1/podcasts/segments/{id}. They match the number field on each line of GET /v1/podcasts/episodes/{id}/transcript, so a transcript can be sliced by segment exactly instead of by arithmetic on float timestamps. A documentation correction ships with them. These docs and the particle_podcast_search_transcripts tool description previously said segments “tile an episode end-to-end” and that “every minute belongs to exactly one segment.” That was wrong, and it is the reason a segment’s end_seconds does not equal the next segment’s start_seconds. Segments are contiguous in transcript lines, not in wall-clock seconds: start_seconds is when a segment’s first spoken line begins and end_seconds is when its last spoken line ends, so consecutive segments are separated by whatever silence sat between those two utterances — usually a fraction of a second, occasionally minutes of music or an ad-stitch window. On episodes reporting start_line/end_line, those intervals contain no transcribed speech and nothing is omitted from them; see the caveat below for older episodes. No existing data changed — this is what segments produced by the current segmenter have always been, now described accurately and made checkable. start_line/end_line are recorded on newly segmented episodes and omitted on everything segmented earlier; backfilling them across the historical corpus is a follow-up and has not shipped. Two different dates are in play here, and conflating them would be misleading:
  • Line-range persistence starts with this release. Every episode segmented before it omits start_line/end_line, however healthy its coverage is.
  • Coverage was fixed on 24 July 2026. Episodes segmented before that came from a version of the segmenter that could genuinely leave transcript lines in no segment, and about 11% of them did. A repair of that cohort is in progress.
So the absence of start_line/end_line does not by itself mean an episode has gaps — the large majority of episodes without them were segmented after the coverage fix and are complete. It only means the partition cannot be verified from the response alone. Where that matters, check the episode transcript directly.

August 2026

Alerts watch exactly one entity

An alert now watches a single entity: particle_alert_create, particle_alert_update, and particle_alert_preview accept exactly one slug in entities, and passing more than one returns an invalid_parameter error telling you to create one alert per entity. The REST endpoints (POST /v1/projects/{projectId}/alerts, PATCH /v1/alerts/{id}, and the preview) enforce the same maxItems: 1. Alerts created before this rule keep their multi-entity watch list and keep matching and delivering; the next write to their entities field must name a single entity. Separately, advertised tool input schemas now carry the numeric and array bounds the server already enforced — minimum/maximum (e.g. window_days 1–30) and minItems/maxItems — across every tool, so agents can self-correct before a failed call instead of after.

Semantic trend counting on the episode timeseries

particle_podcast_get_episode_timeseries takes semantic_search — the counting twin of particle_podcast_search_transcripts’ semantic query, using the same embedding and similarity threshold so the counts agree with search results. “How often do podcasts discuss weight-loss drugs, per week” is now one aggregated call instead of paging search once per period. It adds the same per-bucket mention_count and range total_mentions that keyword_search produces; the two cannot be combined, and semantic_search requires published_after (ranges up to ~2 years). The REST endpoint GET /v1/podcasts/episodes/timeseries gained the matching semantic_search query parameter.

Keyword search now filters instead of only ranking

keyword_search on particle_podcast_search_transcripts and GET /v1/podcasts/episodes/search used to be a ranking signal: the words nudged relevance but nothing was excluded, so a hybrid call could return passages containing none of the words you typed. It is now a filter — every word must appear in the matched passage — which also means a hybrid call is the intersection of semantic_search and keyword_search rather than a blend of both. This changes results for existing calls. A search for QuillBot that returned 100 passages now returns the 21 that actually say “QuillBot”; the rest were semantic neighbours. Where keyword_search is a loose bag of related words that will not co-occur in one passage, the new keyword_match: "ranked" restores the previous behavior. Quoted phrases are unchanged — they have always been exact.

Searches that return nothing now say why

An empty search used to be indistinguishable from “we have no such content”, which left agents unable to tell an over-specified query from an empty corpus. Both surfaces now report the cause, measured by re-running the query with each filter removed in turn. GET /v1/podcasts/episodes/search returns a diagnostics object on an empty page, naming the filter that excluded everything, why it did, and a retry_with parameter delta. particle_podcast_search_transcripts turns the same finding into an actionable error carrying the corrected call, so an agent can retry directly. When the query text genuinely matches nothing, that is still an ordinary empty result rather than an error.

Entity and role filters cover the whole corpus

entity_slug / person_slug / company_slug / role / entity_type filters were resolved against at most 1,000 episodes per entity. For anything widely discussed that was a small, arbitrary slice — Andy Burnham appears in 10,418 episodes, and 90% of them could never be returned. These filters now run natively against the full index, so a filtered search sees every episode. particle_podcast_search_transcripts also gained entity_type, for “discussions of X that mention some book”.

Unresolvable references are rejected, not ignored

Passing a name that matches nothing — company_slug: "x" — used to either empty the results or be silently dropped, depending on whether another parameter happened to resolve. It now returns an unresolved_reference error naming the parameter and the resolve endpoint to call.

Lighter search responses

Search results embedded the parent episode three times per match (once at the top level, once under the segment, once per clip). The redundant copies are gone; the episode on each match is unchanged. If you read segment.episode or clips[].episode out of a search response, read the match’s own episode instead.

Language filter across the podcast tools

particle_podcast_find_mentions and particle_podcast_search_transcripts take a language filter — a two-letter ISO 639-1 code (e.g. "fr") restricting results to episodes of podcasts in that language. “Mentions of Sam Altman on French-language podcasts” is now one call: {"person_slug": "sam-altman", "language": "fr"}. The existing language filter on particle_podcast_list_episodes and particle_podcast_get_episode_timeseries was upgraded from an exact tag match to the same semantics: values are validated (an unknown code is an actionable error rather than a silent empty result) and matched on the podcast’s primary language subtag, so fr covers fr-FR and fr-ca. The REST endpoints GET /v1/podcasts/mentions, GET /v1/podcasts/mentions/timeseries, and GET /v1/podcasts/episodes/search gained the matching language query parameter, with the same subtag semantics applied to the existing filter on GET /v1/podcasts and GET /v1/podcasts/episodes.

company_slug now carries the knowledge-graph slug

particle_podcast_get_sponsors, particle_podcast_get_sponsor_leaderboard, and particle_company_get_podcast_ad_presence used to put a domain-preferred identifier in company_slug (e.g. okx.com). company_slug now carries the actual knowledge-graph slug (e.g. okx), and the previous domain-preferred value moved to a new company_id field — both are valid /v1/companies/{id} keys, but company_slug is the stable one. If your agent parses company_slug out of any of those tools’ JSON output, expect a different value. The rendered markdown still shows company_id, because a domain like okx.com stays recognizable where a generated entity slug can be huntchef-g-11j62z1w0g. The REST endpoint behind the ad-presence tool, GET /v1/companies/{id}/podcast/advertising, gained the matching slug field. particle_podcast_get_episode_ads and its REST endpoint GET /v1/podcasts/episodes/{id}/ads now populate company.slug alongside the existing company.id.

Clips filter by speaker

particle_podcast_list_clips takes a speaker filter: a person slug (from particle_person_resolve), a knowledge-graph entity slug for the same person, or an ID. The listing then returns only clips whose primary speaker is that person — “an insightful Sam Altman clip” is now one call: {"speaker": "sam-altman", "type": "INSIGHTFUL"}. A speaker — or podcast — reference that resolves to nothing now returns an empty listing rather than silently widening back to the full catalog. The REST endpoint GET /v1/podcasts/clips gained the matching speaker query parameter.

July 2026

Speaker roles — a documented, closed set

Every role an episode or transcript tool returns is now one of fourteen documented values — see Episodes → Speaker roles. Previously the field could carry any of ~130 values produced during transcript analysis; those are now normalized into the documented buckets on the way out, so an agent can branch on role without a catch-all. Role arguments — role on the episode and mention tools, speaker_roles on the alert tools — are now case-insensitive, so echoing back a value in the casing it appeared in prose no longer fails validation. CO_HOST is retired and folds into HOST.

Ad timeseries scopes to a show or a network

particle_podcast_get_ad_trends in timeseries mode now takes podcast_slug and publisher_slug, so a company’s ad volume can be charted on one show or across one publisher’s catalog instead of the whole corpus. Both accept a slug or an ID, matching the same filters on the mentions timeseries — which is what makes organic mentions and ad reads comparable under one scope. A reference that resolves to nothing returns zero-filled buckets rather than silently widening back to the full corpus. Omitting both preserves the previous behavior. The REST endpoint GET /v1/podcasts/advertising/timeseries gained the matching podcast_id and publisher_id query parameters.

REST parity: five new tools and six expanded ones

The MCP surface now covers every public GA REST capability. Five new tools:
  • particle_podcast_get_episode_timeseries — time-bucketed episode and mention counts with the same filters as particle_podcast_list_episodes plus keyword_search; the purpose-built answer to “how often is X discussed over time”.
  • particle_podcast_list_clips — browse highlight clips by podcast, type, or engagement score, with a clip_id detail path and include=["transcript"].
  • particle_podcast_get_episode_ads (premium) — every detected ad read inside one episode with sponsor, product, offer, read type, placement, and exact offsets.
  • particle_podcast_get_ad_trends (premium) — sponsor_trends (advertisers ramping up or pulling back) and timeseries (one company’s ad volume over time, split host-read vs pre-recorded).
  • particle_entity_get — a knowledge-graph entity’s profile, closing the loop for place/other slugs from particle_entity_resolve.
And six expanded surfaces (all additive — existing calls are unchanged):

Premium include sections now honor the plan’s premium entitlement

The include sections that carry premium-grade data on standard-tier tools now require a plan with premium endpoints, matching the premium-tier tools themselves: suitability and rankings on particle_podcast_resolve, and bias, suitability, and advertising on particle_podcast_get_publisher. Entitled plans are unaffected. A plan without premium endpoints now receives a premium_required error naming the offending include values — retry without them for the standard sections (the high-level suitability_tier and bias enums on resolve results are standard data and always rendered).

Filter-aware alert previews

particle_alert_preview now accepts the same filters object as particle_alert_create, so the match-frequency estimate reflects what the alert would actually surface once its filters are applied. languages and speaker_roles narrow the historical sweep exactly; relevance and source_popularity are graded at read time and don’t run on historical episodes, so they leave the count unchanged (the estimate is an upper bound when relevance=RELEVANT). The preview cache key keys on the count-affecting filters — changing languages or speaker_roles starts a fresh sweep, while toggling relevance or source_popularity returns the same cached count. Omitting filters preserves the previous unfiltered behavior.

June 2026

Alert filters — narrow what an alert surfaces

Alerts now carry a persistent filters object that narrows what gets surfaced — applied identically to the matches list, the realtime email, and the daily/weekly digests. Four axes: languages (BCP-47 tags), relevance (EVERYTHING vs RELEVANT), source_popularity (ANY vs POPULAR = top 5% of currently-charting podcasts), and speaker_roles for PODCAST_SPEAKER alerts (defaults to GUEST,PANELIST,CORRESPONDENT,AUDIENCE,SOUNDBITE_SPEAKER; sending it on an ENTITY_MENTION alert returns an unprocessable_entity error). particle_alert_create and particle_alert_update accept the filters object directly, and particle_alert_get / particle_alert_list echo the active filter set back so reads expose the current state. Update semantics mirror entities/notifications: passing filters on update replaces the whole set, {} clears all four axes, and omitting it leaves the existing set unchanged. See Alerts → Filtering matches for the per-axis spec and the REST changelog for the wire shape — the same filter object is accepted on POST /v1/projects/{projectId}/alerts and PATCH /v1/alerts/{id}.

Better entity & person resolution

  • particle_entity_resolve and particle_person_resolve now rank across people, companies, and knowledge graph entities with one relevance model (exact name / identifier / known alias → prefix → all-words → fuzzy), replacing the prior name-only substring match. Results are better-ordered and tolerate typos, stock tickers, @handles, and domains. particle_person_resolve also now finds people who aren’t yet linked to a knowledge-graph entity. Each match carries the canonical slug, name, and description (plus a company’s company_domain) — enough to confirm the hit and traverse to the matching get tool.

Alerts

  • Seven alert lifecycle tools — a new default alerts category brings the Alerts feature to the MCP surface: particle_alert_create, particle_alert_list, particle_alert_get, particle_alert_update, particle_alert_delete, particle_alert_preview, and particle_alert_list_matches. Watch a person, company, or other entity and get emailed whenever it’s mentioned on a podcast (ENTITY_MENTION) or appears as a speaker (PODCAST_SPEAKER); preview match frequency before committing; and review the matches an alert has caught.
  • The first writable toolsparticle_alert_create/particle_alert_update mutate and particle_alert_delete is a destructive soft delete, so these are not readOnlyHint: true. They act on the single project your credential is scoped to — there is no project parameter and no way to reach another project’s alerts. Watch targets are passed as the same entity slugs the resolve tools return (resolve a name once, then watch it).
  • External-link attributes in markdown — when particle_podcast_resolve hydrates include: ["external_links"], each platform bullet now appends the attributes the platform reports (audience size, content tallies, verified status, …), so a YouTube channel renders its subscriber and video counts alongside the URL rather than the link alone. The attributes were already present on the output_format: "json" form; this surfaces them in the default markdown.

Agent discovery (auth.md)

  • /auth.md — both hosts now serve the auth.md agent-onboarding document at https://api.particle.pro/auth.md and https://mcp.particle.pro/auth.md: the discovery walkthrough, a credential decision tree, registration steps, errors, and revocation, with the flows Particle Pro deliberately does not offer declared up front.
  • agent_auth block — the AS metadata now embeds a machine-readable registration summary (register_uri, identity and credential types, revocation). Authentication → Agent discovery.
  • REST protected-resource metadatahttps://api.particle.pro/.well-known/oauth-protected-resource now serves an RFC 9728 document describing the REST API resource (previously 404). The MCP document at https://mcp.particle.pro/.well-known/oauth-protected-resource is unchanged.

Skill bundles and exact-tool selection

  • Skill bundles — three ready-made skills (Podcast Intelligence, Company Deep Dive, Guest & People Research): a connection URL scoped to just the tools each workflow needs, plus a copy-paste SKILL.md that teaches your agent the workflow.
  • ?tools= selector — pin a connection to exactly the tools you want advertised: ?tools=particle_podcast_resolve,particle_podcast_search_transcripts. See Tool sets & discovery.
  • More clients in the quickstart — Windsurf, Zed, ChatGPT developer mode, and server-side wiring examples for the Anthropic Messages API and OpenAI Responses API. Quickstart.

Connect without a browser

  • API keys as MCP credentials — the server now accepts the same pp_* project API keys as the REST API (Authorization: Bearer or X-API-Key), so headless agents — cron jobs, the OpenAI Responses API, server-side harnesses — no longer need an interactive OAuth consent. OAuth remains the recommended path when a human is present. Authentication.
  • One-click installs — Cursor and VS Code install buttons in the Quickstart.
  • llms.txt — now opens with instructions for LLM agents: which surface to pick, how the tools compose, and the conventions that save wasted round-trips.

Smarter discovery on connect

  • Conventions taught up front — connecting clients now receive server instructions covering the two things a bare tool list doesn’t show: tools are lean by default and expand via include/mode/format parameters, and every slug a tool returns is a valid input to the other tools (resolve a name once, then traverse).
  • particle_catalog shows each tool’s depth — the catalog menu lists every tool’s expand options (↳ include: … · mode: …), so an agent can see what a tool can really do before fetching its full schema.

25 tools, flat names, opt-in categories

  • Renames (action needed if you call tools by name) — every tool moved to a flat particle_* identifier, e.g. entities/resolve_entityparticle_entity_resolve. The tool reference lists all current names.
  • Consolidationssearch_dialogue and list_clips merged into particle_podcast_search_transcripts: relevant clips now arrive inline on search matches, and a known episode’s full clip list comes from particle_podcast_get_episode with include: ["clips"]. get_entity_mentions became particle_podcast_find_mentions. Boolean flags on get-tools became include arrays.
  • 12 new tools — people profiles and resolution, podcast guests, chart rankings, publishers, listener ratings, and political-bias and brand-suitability analytics. Browse them in the tool reference.
  • Default vs opt-in advertisement — six tool categories advertise on a bare connection; five more (advertising, publishers, ratings, bias, suitability) advertise when you opt in with ?include=. Every public tool stays callable by name either way, and the always-on particle_catalog / particle_call meta-tools let any connection discover and reach the full surface. Tool sets & discovery.

May 2026

Launch

  • The Particle Pro MCP server ships at https://mcp.particle.pro: read-only tools over podcasts, companies, and the knowledge graph, with OAuth 2.1 and markdown outputs designed for agent loops.
  • MCP requests and per-tool usage appear in your organization’s request logs alongside REST traffic.

Missing something? Email api@particle.pro.