YouTube Data API Transcripts: Why captions.download Fails

If you're trying to get transcripts through the YouTube Data API, you've probably already hit the wall: captions.list cheerfully returns caption track IDs for any video, but the moment you call captions.download on one of them, you get a 403. You double-check your API key, add the youtube.force-ssl scope, try OAuth — still 403. This isn't a bug and it isn't a quota problem. It's by design, and the docs bury the lede.
The restriction, precisely
captions.download requires an OAuth token from an account that owns the video. Not any authenticated account — the channel owner (or a content manager with rights to it). The captions endpoints exist so creators can manage subtitle tracks on their own uploads: upload an .srt, fix a typo, delete a bad auto-translation. Downloading captions from arbitrary third-party videos was never part of the contract.
The error message spells it out once you read it carefully:
The permissions associated with the request are not sufficient
to download the caption track.
So the usual escalation path — API key → OAuth as yourself → broader scopes → service account — goes nowhere. Every rung fails the same ownership check. I burned an afternoon on this before accepting it, and judging by the Stack Overflow traffic, so has half the internet.
What captions.list is still good for
To be fair to the Data API: captions.list works with a plain API key and costs about 50 quota units. It tells you which tracks exist, their languages, and whether they're auto-generated (trackKind: "asr") or human-uploaded. That's genuinely useful as a pre-flight check — if a video has no tracks at all, you know up front that you'll need speech-to-text instead of caption extraction. The Data API also remains the right tool for search, playlist enumeration, and metadata. It just won't hand you the transcript text.
Option 1: fetch captions the way the player does
The video player obviously receives caption data without the owner's OAuth token, and libraries like youtube-transcript-api (Python) fetch it the same way. On your laptop this works well and takes three lines.
The caveat shows up in production: YouTube aggressively challenges requests from datacenter IP ranges. Deploy the same three lines to AWS, GCP, or Heroku and you'll start seeing "Sign in to confirm you're not a bot" and IpBlocked errors. I've written up that failure mode and the workarounds separately in why youtube-transcript-api gets blocked in the cloud — short version: it's fixable with residential proxies, but that's a project of its own.
Option 2: a hosted transcript API
If you just want text out of a URL, a hosted endpoint skips both problems — no ownership requirement, no proxy babysitting. youtube2text.org runs the caption fetching behind rotating residential proxies server-side, so your request comes from wherever it comes from:
# grab a free demo key (shared, 5 videos/month per IP)
curl -s https://youtube2text.org/api/demo-key
# fetch a transcript
curl -s "https://youtube2text.org/api/transcribe?url=https://www.youtube.com/watch?v=VIDEO_ID" \
-H "x-api-key: yt_YOUR_KEY"
The response is plain JSON:
{"result": {"videoId": "...", "title": "...", "pubDate": "...",
"content": "full transcript text ...", "contentSize": 12345, "truncated": false}}
If the video simply has no captions, you get TRANSCRIPT_UNAVAILABLE with a 404 — the same situation captions.list would have shown as an empty track list, just detected in one call instead of two.
There's a longer walkthrough with error handling in extracting YouTube transcripts with curl, and a Python version in the youtube-transcript-api migration guide if requests-based code is more your speed.
A sane combined setup
What I'd actually build today, having fought this from both ends:
- Data API for discovery — search, channel uploads, playlists, publish dates. It's reliable and generously quota'd for metadata.
- A transcript endpoint (hosted, or self-scraped with proxies if you enjoy that) for the actual text.
- Speech-to-text as a fallback only for videos with no captions at all.
Trying to force captions.download to do the middle job is the one path that categorically cannot work for videos you don't own — no scope, key, or console setting changes that.
If you want your own key instead of the shared demo one, sign-in is Google-only at youtube2text.org/app/keys — no phone number, no card. The free tier is 5 videos/month; paid plans start at $5.99 for 50.