PodcastsToText
Getting Started

Quickstart

Transcribe your first episode in three calls, with no SDK and no client library.

Three calls, no SDK, no client library. It is HTTP with a bearer token, which every language already speaks.

Before you start

An API key with the transcriptions:write scope, from your dashboard. The API and the MCP server are on Creator, Pro, Studio and Podmaxxing; starting a transcription also needs minutes on the balance. See Authentication for what each scope grants.

1. Start a transcription

Post a link. A podcast episode, a YouTube video, a TikTok, or a direct .mp3: the same field takes all of them, and you do not have to tell us which is which.

Create a transcription
curl -X POST https://podcaststotext.com/api/v1/transcriptions \
  -H "Authorization: Bearer $PTT_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://open.spotify.com/episode/4rOoJ6Egrf8K2IrywzwOMk"}'
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"
  ]
}

An accepted request is 202, even when it is already done. A YouTube video with captions comes back "status": "completed", still with a 202. Branch on status, never on the HTTP code. A request that is refused (bad key, missing scope, plan limit) answers 4xx as usual; the 202 is not universal, it is what acceptance looks like.

2. Wait for it

Poll every 5–10 seconds. Or skip polling entirely and register a webhook, which is what the budget headers would rather you did.

Poll for status
curl https://podcaststotext.com/api/v1/transcriptions/$ID \
  -H "Authorization: Bearer $PTT_KEY"

Without ?include=text you get metadata and a 500-character preview. The full body sits behind transcript_url, so fetching it stays your decision, made knowing how large it is.

3. Read it, in whatever shape you need

Four formats, one parameter
# the full text inline, as JSON
curl "https://podcaststotext.com/api/v1/transcriptions/$ID?include=text" \
  -H "Authorization: Bearer $PTT_KEY"

# subtitles, ready for a video editor
curl "https://podcaststotext.com/api/v1/transcriptions/$ID?format=srt" \
  -H "Authorization: Bearer $PTT_KEY" -o episode.srt

# also: format=vtt, format=txt, format=json, format=docx, format=pdf

The whole thing, in one script

create → poll → export
#!/usr/bin/env bash
set -euo pipefail
BASE=https://podcaststotext.com/api/v1
AUTH="Authorization: Bearer $PTT_KEY"

ID=$(curl -sS -X POST $BASE/transcriptions -H "$AUTH" \
       -H 'Content-Type: application/json' \
       -d '{"url":"'"$1"'"}' | jq -r .id)
echo "started $ID"

until [ "$(curl -sS $BASE/transcriptions/$ID -H "$AUTH" | jq -r .status)" = completed ]; do
  sleep 8
done

curl -sS "$BASE/transcriptions/$ID?format=srt" -H "$AUTH" -o "$ID.srt"
echo "wrote $ID.srt"

Handle failure too. That loop spins forever if the job fails. In real code, break on status == "failed" as well, and give the loop a ceiling.

Where to go next