> ## Documentation Index
> Fetch the complete documentation index at: https://docs.particle.pro/llms.txt
> Use this file to discover all available pages before exploring further.

# Transcripts

> Diarized dialogue, word-level timestamps, entity mentions in context, and SRT export.

Every transcribed episode has three transcript views, each tuned for a different use case:

* **Dialogue** — speaker-attributed lines with sentence-level timestamps. The default. Best for chat-style UIs, LLM context windows, and most reading.
* **Words** — every word individually timestamped. Best for karaoke-style highlighting, precision audio editing, and word-aligned search.
* **Mentions** — lines surrounding mentions of a specific entity, with `is_mention` flags. Best for "what did they say about X?" workflows.

Dialogue transcripts are also reachable scoped to a single segment or clip.

You can answer questions like:

* What exactly was said between minute 12 and minute 15, and by whom?
* Which lines did a particular guest speak, with timestamps for a highlight reel?
* Every passage in an episode where a company is named, with the lines around it.

| Scope                                                    | History                              | Updated                                                                                                                      |
| -------------------------------------------------------- | ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------- |
| Every transcribed episode: dialogue, words, and mentions | The full back catalogue of each show | Available once an episode reaches the `transcribed` milestone, minutes to hours after publication; see [Coverage](/coverage) |

<Note>Available to MCP agents via [`particle_podcast_get_episode`](/mcp/tools/podcasts/podcast-get-episode) with `include: ["transcript"]` (and optional `transcript_format`, `transcript_speaker`, `transcript_start`, `transcript_end`). For mention-style windows, use [`particle_podcast_find_mentions`](/mcp/tools/podcasts/podcast-find-mentions).</Note>

<Note>
  Episode transcript endpoints take an episode id or episode slug, never a podcast slug; `GET /v1/podcasts/segments/{id}/transcript` takes a segment id and `GET /v1/podcasts/clips/{id}/transcript` a clip id. To go from a show to an episode transcript, list the show's episodes with `GET /v1/podcasts/{id}/episodes` and take an episode's `id` from that response.
</Note>

## Dialogue transcript

<CodeGroup>
  ```bash curl theme={"dark"}
  curl "https://api.particle.pro/v1/podcasts/episodes/78cgekLUjCJBUZbj3s5K8Y/transcript" \
    -H "X-API-Key: $PARTICLE_API_KEY"
  ```

  ```js JavaScript theme={"dark"}
  const res = await fetch(
    "https://api.particle.pro/v1/podcasts/episodes/78cgekLUjCJBUZbj3s5K8Y/transcript",
    { headers: { "X-API-Key": process.env.PARTICLE_API_KEY } },
  );
  const { lines } = await res.json();
  ```
</CodeGroup>

```jsonc Response (truncated) theme={"dark"}
{
  "episode_id": "78cgekLUjCJBUZbj3s5K8Y",
  "language": "en",
  "duration_seconds": 4206,
  "lines": [
    {
      "number": 1,
      "speaker": "Scott Galloway",
      "role": "HOST",
      "start_seconds": 0.56,
      "end_seconds": 7.74,
      "text": "This episode is brought to you by The Build Podcast…"
    }
    // …
  ]
}
```

Lines are ordered, 1-indexed, and include speaker name and role. `role` uses the same
[canonical set](/podcasts/episodes#speaker-roles) as the speakers endpoint. Each line's
`number` is the same 1-indexed value a segment's `start_line`/`end_line` refer to, so you
can slice this list by segment exactly.

Note that the first lines of many episodes are sponsorship reads — fetch `/segments` to see
which time ranges are tagged `AD` if you want to skip them. When skipping by time, skip to
the **next** segment's `start_seconds` rather than the `AD` segment's `end_seconds`: an ad
segment ends at the last spoken word of the read, so the silence after it is not covered by
any segment. That is safe where the segments report `start_line`/`end_line`: skipping by
`end_line` sidesteps the timing question entirely. Where they are absent, the interval can
contain unassigned speech, so skipping to the next `start_seconds` may discard real content
— check the episode transcript for lines in that window before dropping it. See
[segments](/podcasts/segments-and-clips#segments).

## Output formats

Use the `format` query parameter:

<Tabs>
  <Tab title="Dialogue (default)" icon="comments">
    Structured JSON with speaker attribution, roles, and timestamps per line.

    ```bash theme={"dark"}
    curl ".../transcript?format=dialogue"
    ```

    Best for: building conversation UIs, speaker analysis, programmatic processing.
  </Tab>

  <Tab title="Plain text" icon="align-left">
    `Speaker: text` lines, one per turn.

    ```bash theme={"dark"}
    curl ".../transcript?format=text"
    ```

    Best for: LLM context windows, full-text display, search indexing.
  </Tab>

  <Tab title="SRT subtitles" icon="closed-captioning">
    Standard SubRip format with speaker labels.

    ```bash theme={"dark"}
    curl ".../transcript?format=srt"
    ```

    Best for: video players, caption rendering, accessibility tools.
  </Tab>
</Tabs>

## Filter by speaker or time range

Extract everything one person said:

```bash theme={"dark"}
curl ".../transcript?speaker=Kara+Swisher" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

Extract a time range:

```bash theme={"dark"}
# Minute 5 to minute 15
curl ".../transcript?start=300&end=900" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

Combine — exactly what one person said during one segment:

```bash theme={"dark"}
curl ".../transcript?speaker=Scott+Galloway&start=1435&end=2228&format=text"
```

## Word-level transcript

For per-word timing — karaoke-style highlighting, precision editing, word-aligned search:

```bash theme={"dark"}
curl "https://api.particle.pro/v1/podcasts/episodes/78cgekLUjCJBUZbj3s5K8Y/transcript/words?start=0&end=10" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

```jsonc Response (truncated) theme={"dark"}
{
  "episode_id": "78cgekLUjCJBUZbj3s5K8Y",
  "language": "en",
  "words": [
    { "text": "This", "type": "word", "start_seconds": 0.56, "end_seconds": 0.74, "speaker": "Scott Galloway" },
    { "text": " ",    "type": "spacing", "start_seconds": 0.74, "end_seconds": 0.82, "speaker": "Scott Galloway" },
    { "text": "episode", "type": "word", "start_seconds": 0.82, "end_seconds": 1.12, "speaker": "Scott Galloway" }
    // …
  ],
  "has_more": false
}
```

<Warning>
  Word-level transcripts for long episodes can be very large — a feature-length episode runs to tens of thousands of entries. Pass `start` and `end` to clip a time range, `limit` to bound the page size, and `exclude_spacing=true` if you don't need the inter-word whitespace tokens.
</Warning>

### Query parameters

| Param             | Default            | Description                                                      |
| ----------------- | ------------------ | ---------------------------------------------------------------- |
| `start`           | `0`                | Start time in seconds for time-range clipping.                   |
| `end`             | end of episode     | End time in seconds for time-range clipping.                     |
| `limit`           | unset (return all) | Max words per page (1–5000). Omit to return every matching word. |
| `cursor`          | —                  | Opaque cursor from a previous `cursor` response field.           |
| `exclude_spacing` | `false`            | Drop `type:"spacing"` tokens. See below.                         |

### Spacing tokens

Roughly half of the entries in a typical word transcript have `type:"spacing"` and `text:" "` — these represent the silence *between* spoken words, with their own `start_seconds`/`end_seconds`. They're useful when timing matters: caption/subtitle UIs that need precise pause durations, karaoke-style word highlighting against playback, or reconstructing inter-word silences for audio alignment. NLP, search, and LLM consumers should pass `exclude_spacing=true` to skip them — it roughly halves the payload.

### Pagination

When you pass `limit`, the response includes a `cursor` and `has_more: true` until the last page:

```bash theme={"dark"}
# First page
curl ".../transcript/words?limit=1000&exclude_spacing=true" \
  -H "X-API-Key: $PARTICLE_API_KEY"

# Next page — paste the cursor from the prior response
curl ".../transcript/words?limit=1000&exclude_spacing=true&cursor=r.AbCdEf" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

The `speaker` field is the identified speaker name (e.g. `Scott Galloway`). When a speaker hasn't been resolved to a name, the raw STT label (`speaker_0`, `speaker_1`, …) is returned instead.

## Transcript mentions

The most useful transcript view when you care about a specific person, company, or topic. Returns the dialogue lines around every mention of an entity in one episode, with `is_mention: true` on the lines that actually contain the mention and surrounding lines for context.

<CodeGroup>
  ```bash curl theme={"dark"}
  curl "https://api.particle.pro/v1/podcasts/episodes/78cgekLUjCJBUZbj3s5K8Y/transcript/mentions?entity_id=sam-altman" \
    -H "X-API-Key: $PARTICLE_API_KEY"
  ```
</CodeGroup>

```jsonc Response (truncated) theme={"dark"}
{
  "episode_id": "78cgekLUjCJBUZbj3s5K8Y",
  "entities": [
    {
      "entity": {
        "id": "5MBAHcKUujL2dzPXrgfQ8E",
        "slug": "sam-altman",
        "name": "Sam Altman"
      },
      "total_mention_count": 5,
      "mention_variants": ["Sam Altman"],
      "mentions": [
        {
          "lines": [
            { "number": 363, "speaker": "Kara Swisher", "text": "Uh, let's go on a quick break.", "is_mention": false, "start_seconds": 1232.53, "end_seconds": 1233.69 },
            { "number": 364, "speaker": "Kara Swisher", "text": "When we come back, Elon Musk and Sam Altman head to court.", "is_mention": true, "start_seconds": 1233.79, "end_seconds": 1236.51 },
            { "number": 365, "speaker": "Kara Swisher", "text": "Big story, actually.", "is_mention": false, "start_seconds": 1236.65, "end_seconds": 1238.09 }
          ],
          "start_seconds": 1231.47,
          "end_seconds": 1246.56
        }
        // …
      ]
    }
  ],
  "has_more": true,
  "cursor": "r.AQAAABk"
}
```

Use `start_seconds` / `end_seconds` to deep-link into the audio, or feed the line text into an LLM with full speaker context.

### Chunking semantics

The endpoint groups transcript lines into context **windows**:

* `context_lines` (default `2`, max `20`) is the radius of each window. A mention on line `i` produces the closed range `[i - context_lines, i + context_lines]`, clamped at the transcript boundaries — so a single mention yields up to `2 * context_lines + 1` lines.
* Adjacent or overlapping windows **merge** into a single window. Two mentions within `context_lines` of each other produce one entry with multiple `is_mention: true` lines, not two entries.
* `total_mention_count` is the unfiltered count of dialogue lines containing at least one mention of the entity in the episode. It is independent of pagination, and equals the sum of `is_mention=true` flags across the un-paginated `mentions[]`.
* Mention matching is a case-sensitive substring match against `mention_variants` (the entity's canonical name plus any annotated aliases).

### Pagination

| Caller shape         | Pagination axis        | Cursor refers to                    |
| -------------------- | ---------------------- | ----------------------------------- |
| `entity_id` provided | `mentions[]` (windows) | offset into windows for that entity |
| `entity_id` omitted  | `entities[]`           | offset into entity list             |

When `entity_id` is set, exactly one entity is returned and its `mentions[]` is paged. When `entity_id` is omitted, each entity in the page returns **all** of its mentions; deep-paging through a single entity's mentions requires re-issuing with that `entity_id`.

`limit` defaults to `25` and is capped at `100`. Pass the `cursor` from a previous response to fetch the next page; `has_more` indicates whether more results exist.

```bash theme={"dark"}
curl "https://api.particle.pro/v1/podcasts/episodes/78cgekLUjCJBUZbj3s5K8Y/transcript/mentions?entity_id=sam-altman&limit=25&cursor=r.AQAAABk" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

## Segment and clip transcripts

Segments and clips have their own transcript endpoints scoped to that time range:

```bash theme={"dark"}
# Segment transcript
curl "https://api.particle.pro/v1/podcasts/segments/{segment_id}/transcript" \
  -H "X-API-Key: $PARTICLE_API_KEY"

# Clip transcript with SRT export
curl "https://api.particle.pro/v1/podcasts/clips/{clip_id}/transcript?format=srt" \
  -H "X-API-Key: $PARTICLE_API_KEY"
```

Both accept the `format` query parameter (`dialogue`, `text`, or `srt`).

### Timestamps on these two endpoints are segment-relative

<Warning>
  `/segments/{id}/transcript` and `/clips/{id}/transcript` return timestamps
  **relative to the start of that segment or clip** — the first line begins at
  `0.0` — and renumber their lines from `1`. This pairs correctly with the
  segment's own MP3, which also starts at zero, and it is what makes the `srt`
  export usable as a subtitle track.

  `/v1/embed/clips/{id}/transcript` behaves the same way, for the same reason.

  The episode-level surfaces return **absolute** episode timestamps.
  `/episodes/{id}/transcript` and `/episodes/{id}/transcript/preview` also carry
  the episode's own line numbers. `/episodes/{id}/transcript/words` is
  word-level: its entries have timestamps and a speaker but no line number, so
  they cannot be joined to a segment's `start_line`/`end_line` directly — use the
  timestamps for that.

  So a segment's `start_line`/`end_line` index the **episode** transcript, not the
  per-segment one. If you are stitching segment transcripts back into an episode
  timeline, either add `start_seconds` back to each line or — simpler — slice the
  episode transcript by `start_line`/`end_line` instead.
</Warning>

## Related

* [Episodes](/podcasts/episodes) — discovery and sub-resources
* [Segments & clips](/podcasts/segments-and-clips) — get a clip ID, then its transcript
* [Knowledge graph → entities](/knowledge-graph/entities) — pick the entity to mention-search
