Introducing the transcribe.so API: speech-to-text as a Bearer token

Transcribe.so(Updated May 19, 2026)
transcribe so apitranscription apispeech to text apideveloper apiqwen3-asr-flash apiwhisper api alternativeChatGPT Custom GPTClaude Custom ConnectorMCP

You can now transcribe audio with transcribe.so without ever opening the dashboard.

curl -X POST https://transcribe.so/api/v1/transcriptions \
  -H "Authorization: Bearer tsk_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "source": "external_url",
    "url": "https://example.com/podcast.mp3",
    "pipeline_code": "qwen3-asr-flash-filetrans"
  }'

That's the whole thing. One Bearer token, one POST, you're transcribing.

Why an API

We built transcribe.so for people who care about transcript quality — meeting notes, podcasts, courseware, voice memos. But the more we shipped the dashboard, the more we kept hearing the same request from the people who use it most:

"I love the output. I just don't want to drag a file into a browser tab forty times a day."

Fair. Some workflows want a UI. Most automation wants a curl.

The use cases we've heard so far:

  • Pipelines that ingest podcasts — auto-transcribe new episodes the moment they hit your S3 bucket.
  • Meeting bots — transcribe Twilio recordings or Zoom dumps as they come in, no human in the loop.
  • Journalist workflows — drop interview audio in a folder, get back a searchable transcript with chapters.
  • Voice-memo automations — your phone records, your laptop transcribes, your second brain stores it.

So here it is.

What's the same as the dashboard

Almost everything.

  • Same models. Qwen3-ASR-Flash, GPT-4o-transcribe-diarize, Voxtral. Pick the one that fits the request — pass pipeline_code and you're set.
  • Same pricing. No "API tier" markup. Self-hosted transcription is unlimited on your plan; premium models (GPT-4o, Voxtral) draw from your wallet at the same pay-as-you-go rate the pricing page shows for web jobs.
  • Same wallet. Monthly premium credit drains first; top-up balance covers the rest, exactly as it does for web jobs.
  • Same downstream pipeline. Sections, chapters, summaries, semantic search, Q&A with citations — all of it lives behind GET /transcriptions/:id/result. You're not getting a stripped-down API output. You're getting the full pipeline.

The whole point of the API is that we didn't fork it. The Bearer token swaps in for the session cookie, and you call the same code path the web UI does.

What's different

The handful of things you'd expect.

Bearer auth. Every request carries Authorization: Bearer tsk_live_.... No cookies, no CSRF, no SDK, no setup beyond pasting a key into your env. Keys live at transcribe.so/settings/api-keys, and we show the plaintext exactly once.

Async with polling. Submit, get back a transcription ID and status: "processing", then poll GET /transcriptions/:id until you see "completed" or "failed". We tried to make this honest: the same statuses you see on the dashboard are the statuses the API returns. (Webhooks ship next — you won't have to poll forever.)

Idempotency-Key header. Send a UUID and we'll cache the response for 24 hours. Retrying on a network blip won't double-charge you or queue the job twice. Standard Stripe pattern.

Per-key visibility. Each key shows you this month: $X.XX and all time: $X.XX on the dashboard. If you hand a key to a teammate or paste one into a script you're not sure about, you can see exactly what it spent. Revoke one and the others keep working.

We deliberately didn't ship per-key spending caps in v1. The wallet itself is the cap — you can't spend money you don't have, and an account-level monthly hard cap is the cleaner pattern when we get there. (If your team needs per-key caps before then, tell us.)

A worked example

Here's a Python script that watches a folder, transcribes every new audio file by URL, and saves the JSON result next to it. It's small enough to read in one breath:

import os, time, requests, json, pathlib

API = "https://transcribe.so/api/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['TRANSCRIBE_API_KEY']}"}

def transcribe(url: str) -> dict:
    r = requests.post(f"{API}/transcriptions", headers=HEADERS, json={
        "source": "external_url",
        "url": url,
        "pipeline_code": "qwen3-asr-flash-filetrans",
    })
    r.raise_for_status()
    job_id = r.json()["id"]

    while True:
        s = requests.get(f"{API}/transcriptions/{job_id}", headers=HEADERS).json()
        if s["status"] in ("completed", "failed"):
            break
        time.sleep(3)

    if s["status"] == "failed":
        raise RuntimeError(f"transcription failed: {s.get('error')}")

    return requests.get(f"{API}/transcriptions/{job_id}/result", headers=HEADERS).json()


if __name__ == "__main__":
    out = transcribe("https://example.com/podcast.mp3")
    pathlib.Path("transcript.json").write_text(json.dumps(out, indent=2))
    print(f"saved {len(out['segments'])} segments")

Six lines of glue, full transcript with chapters and sections on disk. The same shape works in a Cloudflare Worker (Twilio webhook → API call → write to KV), a GitHub Action (new podcast episode in a release → transcribe → comment on the PR), or a long-running n8n flow.

What's next

The roadmap, in priority order:

  1. File uploads. v1 shipped with external-URL transcription. Direct uploads via a presigned S3 PUT / TUS resumable land at POST /api/v1/uploads for cases where you don't want to host the audio yourself.
  2. Webhooks. transcription.completed and transcription.failed, signed with HMAC, with exponential-backoff retries. Polling works; webhooks are nicer.
  3. OpenAPI spec. Already live at app/api/v1/openapi.yaml (also drives the ChatGPT Custom GPT Actions schema). First-party Python and TypeScript SDKs come once the surface fully stabilizes.
  4. Account-level monthly cap. A single "don't let the whole account spend more than $X this month" hard limit. Applies equally to web UI and API.

Update: now also a public ChatGPT GPT

The same API now powers a public Custom GPT in the ChatGPT store. Per-user OAuth — each ChatGPT user signs in with their own transcribe.so account, so they pay against their own wallet, not yours. Paste a YouTube link, get key moments + Q&A with grounded timestamps, ask follow-ups in plain English. Exactly what the API does, packaged for non-developers.

Update: now also a Claude Custom Connector

The same engine is also live as a public Claude Custom Connector. Install once at https://transcribe.so/mcp, sign in with your own transcribe.so account, and ask Claude to transcribe URLs or pull chapters and Q&A in any conversation. Per-Claude-user OAuth means each user pays from their own wallet, not the connector author's. Claude's 150K-character tool budget is roughly 3x what the GPT gets, so responses come back richer in one tool call. Works on every paid Claude tier, including Free (which is capped at one custom connector but still allowed).

If you have a use case that doesn't fit any of the above, we want to hear it. The API is going to be shaped by what people actually build with it.

Until then — grab a key, paste it into your script, and let us know what you ship.

Ready to transcribe your own content?

No credit card required. Cancel anytime.

See it in action

Real output from a real transcription

Browse chapters, ask questions, and explore search results from an actual transcript.

How to Quit Your Job (and Find Work You Actually Love)
Ali Abdaal
Contents
18 chapters · 57 sections
1Why I quit my high-paying job with no plan
2The shame of walking away from success
3Stop accepting low-grade suffering at work
4Are you wired for the pathless path?
5The math behind quitting your job safely
6Use time off to rediscover who you are
7How to fund your freedom on a budget
8Your income streams will evolve over time
9Turn your skills into immediate cash flow
10Treat your career break like a life MBA
11Passion doesn't mean work is easy
12Align your daily actions with your ideal life
13Focus on your mode, not your niche
14Declare yourself retired with the skip test
15Handling family criticism of your career choices
16Would you trade wealth for total freedom?
17Get comfortable with feeling cringe
18Why traditional job security is a myth
Ask this video
Answer
Paul left because the work had quietly stopped fitting who he was, not because of a single dramatic event. Early on he chased prestige and big salaries, optimizing for impressive internships and the markers of success [00:59–02:18]. By around thirty-two the job had drained his energy and passion, and quitting was mostly about escaping that misalignment and getting himself back [04:37–06:04]. When he ran a self-assessment, he realized he'd drifted from the goals he set in grad school, to avoid becoming money-obsessed and to keep his sense of humor, which made clear how far off course he'd gone [06:05–07:55]. The decision was less “follow your dream” and more “stop betraying your own values.”

Command Palette

Search for a command to run...

No credit card required. Cancel anytime.