Read, list and delete
Poll for status, read the text, page through everything the key can see, and remove a transcript.
/api/v1/transcriptions/{id}scope: transcriptions:readOne transcript: its status while it runs, its content once it is done.
Query parameters
| Field | Type | Required | Description |
|---|---|---|---|
include | string | No | Set to text for the full transcript body inline. Omitted, you get a 500-character preview and a URL. |
format | string | No | One of srt, vtt, txt, json, docx, pdf. Returns the file itself. See Exports. |
speakers | boolean | No | Label each cue with its speaker in srt, vtt and txt, and head each paragraph with the name in docx and pdf. json always carries a speaker field when the transcript has one, so this parameter does not affect it. |
timestamps | boolean | No | docx and pdf only: head each paragraph with its start time. srt and vtt are timings already, and json carries start and end on every cue. |
The body sits behind transcript_url rather than inline, so a status poll does not carry tens of thousands of words you already had.
| Status | Meaning | What to do |
|---|---|---|
processing | Running. Typically a fraction of the episode length. | Poll every 5–10s, or use a webhook. |
completed | Done. transcript_url is populated. | Fetch it, or request a format. |
failed | The audio could not be transcribed. | Check the source URL is still reachable before retrying. |
Prefer webhooks to polling. A transcription.completed webhook removes the poll loop entirely, and polling is what the daily request budget is mostly spent on. See Webhooks.
Getting the text
Quote the URL. In a shell an unquoted & backgrounds the command and the second parameter is lost, so ?format=srt&speakers=true becomes ?format=srt with no error to say so.
A long translation arrives over several calls. Translation is never condensed, it is the whole episode, but a long one does not fit inside a single request. When parts remain, the response is 202 with status: "translating", parts_done and parts_total, and translation: null. POST again with the same language to continue: finished parts are stored and are never charged twice. A 200 with status: "completed" carries the full text.
A very long episode is condensed before the AI tools run on it. An episode small enough to send a model whole is sent whole. One that is not is split into parts, each part condensed into a structured digest, and the digests used in order as a stand-in for the whole episode, so the output covers the end of the episode, not just the beginning. When that happens the response carries source: "digest". Translation is never condensed: it is chunked and translated in full.
Time-coded formats need timestamps. srt, vtt and json need a transcript with segments. Free-tier transcripts are plain text and return 400 for those formats. txt, docx and pdf work for any transcript.
Listing them
/api/v1/transcriptionsscope: transcriptions:readEverything this key’s account can see, newest first, cursor-paginated. No transcript bodies, ever: not even previews.
| Field | Type | Required | Description |
|---|---|---|---|
limit | number | No | How many to return. Default 20, maximum 50. |
status | string | No | Filter to processing, completed or failed. |
show_id | string | No | Only transcripts belonging to one connected show. |
cursor | string | No | The next_cursor from a previous page. |
next_cursor is opaque: pass it back verbatim; null means the end. It identifies a row by created_at and id, so rows created in the same millisecond still page correctly and nothing is skipped or repeated mid-page.
Deleting one
/api/v1/transcriptions/{id}scope: transcriptions:writeRemoves the transcript and its stored files, answering { "id": "…", "deleted": true }. A 404 means no such id or it belongs to another account; the two are not distinguished.
Permanent, and it does not refund. Credits were spent when the audio was transcribed, so deleting the result does not return them, and re-transcribing the same episode spends them again.