Skip to main content
Search the podcast catalog by what is said in episodes — by meaning (semantic_search), by exact phrase (keyword_search), or both at once (hybrid ranking). This is THE way to retrieve relevant dialogue, segments, and clips: each result is one segment of one episode with bounded transcript windows pinpointing the highest-relevance lines, plus any highlight clips that overlap the segment, inline on the match.
This tool replaces the old search_dialogue and list_clips tools. There is no separate clip-search tool — relevant clips arrive on these matches. A known episode’s full clip list is particle_podcast_get_episode with include: ["clips"].

Segments vs clips

  • Segments partition an episode’s transcript — where start_line/end_line are present, every spoken line belongs to exactly one segment and one segment’s end_line + 1 is the next one’s start_line. They are contiguous in transcript lines, not in wall-clock seconds: the seconds between one segment’s end_seconds and the next’s start_seconds contain no transcribed speech. These matches do not carry the line ranges themselves — fetch them with particle_podcast_get_episode and include: ["segments"], where their absence marks an episode segmented by an earlier version, a small share of which do leave lines uncovered. Search ranks segments and returns the matching ones.
  • Clips are sparse, engagement-ranked highlights that overlap some segments. When a clip overlaps a ranked segment, it appears under an Overlapping clips: line on that match.
Use this for “find dialogue about a topic”. For “every line naming a person or company” use particle_podcast_find_mentions instead — person_slug and company_slug here narrow ranked results, they don’t drive the ranking. What semantic_search is not good at:

Crafting the query

semantic_search and keyword_search do different jobs. Most disappointing results come from using one where the other belongs. semantic_search carries the idea. Write it as a sentence describing what should be discussed, in the words a speaker would actually use. It is paraphrase-tolerant, so it finds the topic however it happens to be phrased. keyword_search carries words that must be literally spoken. Every word must appear in the same passage, so it is for one or two exact tokens — a ticker, a product name, a drug name — not for a description. A sentence here returns nothing, because no passage contains all of those words. Use both when a topic must also contain an exact term. The result is their intersection, which is narrow by design. If it comes back empty, keyword_match: "ranked" relaxes the keyword side back to a relevance hint.
Never put a name in semantic_search. Resolve it with particle_person_resolve, particle_company_resolve, or particle_entity_resolve and pass the slug. Searching for "Sam Altman" as text finds passages that sound like him; person_slug: "sam-altman" finds the episodes actually featuring him.

Start broad, then narrow

Every filter compounds, and each one can remove all results on its own. Issue the query with semantic_search alone first, confirm the topic has coverage, then add filters. If a search returns nothing because of your filters, the tool returns an error that names the specific parameter responsible and the corrected call to make — it is measured by re-running your query with each filter removed, not guessed. Act on it rather than re-issuing variations of the same query. When the query text genuinely matches nothing, you get an ordinary empty result instead, which is the signal to rephrase semantic_search. role is the filter most likely to surprise: guest/host/panelist/ correspondent mean the person spoke in that specific capacity, speaker means they spoke in any of them, and mention means they were talked about rather than speaking. Omitting role covers speaking and mentions both, and is almost always what you want. These are the same values /v1/podcasts/episodes accepts.

Did they say it?

person_slug keeps episodes featuring a person, so a match can be the host quoting them or a clip of them on another show. speaker keeps only passages in which the person speaks a line, and only their lines are bold matches: speaker: "sam-altman" with keyword_search: "\"irrelevant marketing term\"" returns the interview where he said it, not the shows that replayed it. Their own lines must carry every quoted phrase (a phrase may run across their consecutive lines), and every unquoted word under keyword_match: "required". A soundbite of them played on someone else’s show never counts. Drop speaker to see who repeated the line. An empty result does not prove they never said it: narrow with podcast_slug, episode_slug or dates, or try semantic_search. speaker cannot be combined with the guest_ filters.

Guests’ expertise

The guest_ filters keep matches from episodes with a guest of that expertise, with codes from particle_expertise_resolve: what practicing cardiologists said about a drug is semantic_search plus guest_occupation="cardiologists" and guest_practicing=true. When the question restricts who speaks (“medical professionals”, “economists”), resolve the words to codes first and filter by them: never put the profession in semantic_search, and on unresolved_reference resolve and retry rather than dropping the filter. The filters keep segments in which a matching guest speaks, with the preview opening on that guest’s first line, so a window can also carry the host’s lines: read each line’s speaker in the match’s **Speaker:** rows before attributing it to a credential. To keep only the lines a matching guest spoke about a named person or company, use particle_podcast_find_mentions with the same filters. Like every filter here, a guest_ filter that empties the results is named in the error, with the call to make instead. All guest_ filters describe the same guest. A word no occupation names may name a field or a capacity instead, and filters by it: guest_occupation="oncologists" is field 3211, guest_occupation="founders" is guest_capacity="executive_founder". An occupation or field that names nothing returns unresolved_reference with the closest codes as did-you-mean suggestions; guest_practicing without a detailed SOC 2018 guest_occupation returns validation_error.

Inputs

At least one of semantic_search or keyword_search is required.

Output

A markdown document with ## Dialogue matches (N) and one ### Segment title section per match. Each section carries bulleted KV rows: - **Episode:**, - **Podcast:**, - **Podcast slug:**, - **Published:**, - **Episode slug:**, - **Segment id:**, - **Segment span:** (the segment’s start–end seconds), - **Segment type:**, - **Match:** (the source: semantic, keyword, or hybrid, with the relevance score when the ranker reported one — comparable within one response, not across queries), then one **Speaker:** Name (slug, ROLE) — Title (code) · standing · seniority row per identified person speaking in the windows (the occupation codes are what guest_occupation takes; a speaker without a profile shows only name, slug and role), then one or more bullets formatted Window @ start–end (or Preview @ … for truncated windows) followed by indented dialogue lines. Every line that matched the query is rendered with the entire Speaker: text wrapped in bold (a window can contain several matched lines); surrounding context renders as **Speaker:** text. When highlight clips overlap the segment, they appear under an Overlapping clips: line as bullets formatted Title (TYPE, score N) @ start–end — clip ID: <id>. When more pages exist, a horizontal rule and a **Cursor:** <value> line are appended. Bolded lines mark the lines that ranked for the current query (a window can contain several). This is a different signal from the bolding in particle_podcast_find_mentions, where a bolded line means “this line names the resolved entity” — they answer different questions and can both be true within the same episode for different reasons. Sample (semantic_search="how AI affects the labor market", limit=2):
The string after - **Episode slug:** is what you can feed into particle_podcast_get_episode for the full transcript or clip list, or into particle_podcast_find_mentions (format=detail, episode_slug) for mention windows around a specific person.

Screening many results

The default page is sized for reading: each match carries its dialogue, and a page of ten runs tens of kilobytes. When the job is to decide which segments matter before reading any — a sweep over a list of companies, a set of themes, or a run of dates — pass format: "compact". The ranking, ordering, has_more, and cursor are identical; each match keeps its identity rows and gains - **About:**, the segment’s one-line description, and loses its windows and clips. The transcript load and line scoring that produce windows are skipped, so the page is a fraction of the size and returns sooner. Sample (semantic_search="how AI affects the labor market", format="compact", limit=2):
Read dialogue only for the segments that survive the screen: particle_podcast_get_episode with include: ["transcript"] and transcript_start/transcript_end set to the segment span, or this tool again in the default format with episode_slug narrowed to that episode. The segment id is the citation; it is the same id include: ["segments"] lists on the episode and the REST segment endpoints take.

Example

To screen a company’s coverage this week before reading any of it:
What practicing cardiologists said about GLP-1 drugs:
For hybrid ranking, populate both semantic_search and keyword_search: