PodcastsToText
Getting Started

API at a glance

The whole API on one screen: what it does, one request, one response, and every parameter.

Convert podcasts, videos and audio files to text over HTTP. Post the link a human would paste and get back a transcript with speaker labels and word-level timings.

  • Speaker diarization: distinct speakers detected and labelled, on by default.
  • Over 100 languages, auto-detected. Pass a hint only when you want to override it.
  • One field for every source. No separate endpoint per platform, and no scraping on your side.
  • Six output formats from the same transcript (JSON, TXT, SRT, VTT, DOCX, PDF) at no extra cost.
  • One credit per minute of audio. Creator is $14 a month for 5 hours; a YouTube video with captions costs nothing at all.

API usage

You need an API key. Create one at your dashboard, then send it as a bearer token. There is no SDK to install.

Start a transcription
const res = await fetch('https://podcaststotext.com/api/v1/transcriptions', {
  method: 'POST',
  headers: {
    'Authorization': 'Bearer YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    url: 'https://open.spotify.com/episode/4rOoJ6Egrf8K2IrywzwOMk',
    language: 'en',
    speaker_labels: true,
  }),
});

const { id, status } = await res.json();
// status is "processing" — poll, or take a webhook

API response

Creating a job answers immediately with an id. Transcription is asynchronous, so nothing here waits on the audio.

202 Accepted
{
  "id": "9f3c8e21-4b7a-4c19-9f2e-1d8a6c3b5e07",
  "status": "processing",
  "source_type": "spotify",
  "message": "Transcription started. It usually takes a fraction of the episode length.",
  "next_actions": [
    "GET /api/v1/transcriptions/9f3c8e21-… to check status: poll every 5-10s until status is \"completed\"",
    "GET /api/v1/transcriptions/9f3c8e21-…?include=text once completed"
  ]
}

Then read it back. Without include=text you get metadata and a 500-character preview; with it, the whole body.

Read the finished transcript
curl "https://podcaststotext.com/api/v1/transcriptions/$ID?include=text" \
  -H "Authorization: Bearer YOUR_API_KEY"

# or get a file instead of JSON
curl "https://podcaststotext.com/api/v1/transcriptions/$ID?format=srt" \
  -H "Authorization: Bearer YOUR_API_KEY" -o episode.srt

Branch on status, never on the status code. Create always answers 202, even when the work is already finished: a YouTube video with captions is read inline and comes back "status": "completed". That is a property of the video, not of your request.

API parameters

POST https://podcaststotext.com/api/v1/transcriptions takes the following:

FieldTypeRequiredDescription
urlstringYesThe thing to transcribe: a Spotify or Apple Podcasts episode link, a YouTube or TikTok URL, or a direct audio file. Required unless you send audio_url.
audio_urlstringNoA direct audio file you have already resolved yourself. Skips resolution entirely.
languagestringNoISO code such as en or de. Omitted means autodetect, which is right almost always.
speaker_labelsbooleanNoDetect and label distinct speakers. Defaults to true. Paid plans only.
titlestringNoOverrides the title resolved from the platform.
duration_secondsnumberNoOnly with a direct audio_url, where the file would otherwise be probed for its length.

And on the way back out, GET /api/v1/transcriptions/{id} takes include=text for the full body, or format= for a file:

`format`Content-TypeTimingsNeeds a paid-plan transcript
jsonapplication/jsonYesYes
txttext/plainNoNo
srtapplication/x-subripYesYes
vtttext/vttYesYes
docxOOXMLWhere presentNo
pdfapplication/pdfWhere presentNo

json is the richest shape: segments with start, end, text and speaker. The last column is about the transcript, not your plan today: free-tier transcripts are stored without timings, so a time-coded export of one stays impossible after you upgrade.

Three things that are not standard

What it means for your client
Every error carries how_to_fixA concrete next action, not just a code. An automated client that reads it stops retrying calls that can never succeed.
RateLimit-* headers on successPace on RateLimit-Remaining rather than waiting to be refused. A client that first learns its limit at the 429 is the retry storm the limit exists to stop.
next_actions on successThe legal next call, in the response. An agent learns the flow without re-reading these docs mid-task.

Where next

The machine-readable spec is at `/api/v1/openapi.json` — point a generator at it instead of writing a client.