Back to blog
Guide·Jul 29, 2026·7 min read

How to get YouTube transcripts programmatically in 2026

The official YouTube Data API tells you a caption track exists, then refuses to give it to you - captions.download needs the video owner's OAuth token. What that means in practice, and the three approaches that actually work.

Chandler Caseyby Chandler Casey

There is a fact about YouTube that surprises almost everyone who tries to automate transcripts, and it is not written plainly anywhere in Google's documentation: the official YouTube Data API will tell you a caption track exists, and then refuse to give it to you.

That single asymmetry explains most of the confusion in this space - why the popular Python library works on your laptop and dies in production, why "just use the official API" is bad advice, and why an entire category of third-party services exists at all. This guide walks through what actually works in 2026, starting with the official route, because you should understand why it fails before you pay anyone to work around it.

(Disclosure: we build TranscriptFetch, one of the options below. Every technical claim here is checkable against public documentation, and I have linked it so you can.)

The official API: what it does and does not do

The YouTube Data API v3 has two caption endpoints, and the difference between them is the whole story.

captions.list tells you which caption tracks exist on a video. It costs 50 quota units per call and returns track metadata - language, whether it was auto-generated, when it was updated. No transcript text.

captions.download returns the actual caption content. Notably, it does not appear in Google's published quota cost table at all, while captions.list (50) and captions.insert (400) both do. And per Google's own documentation, it requires an OAuth 2.0 token from the channel that owns the video.

Not an API key. Not your OAuth token. The owner's.

code
GET /youtube/v3/captions/{id}?key=API_KEY          → 401 Unauthorized
GET /youtube/v3/captions/{id}  + your OAuth token  → 403 Forbidden
GET /youtube/v3/captions/{id}  + owner's token     → the captions

Both failures are worth distinguishing, because they send people down different debugging paths. An API key alone gets a 401 - the endpoint will not talk to you without OAuth at all. Authenticate properly with your own Google account and you get a 403 instead, which reads like a permissions bug you could fix by requesting another scope. You cannot. The only token that works belongs to the uploader.

The asymmetry is sharper than it looks: captions.list works fine with a plain API key. You can enumerate a video's caption tracks, see their languages, see whether they are auto-generated - all without OAuth. It is only reading the text that is gated.

So the official API supports exactly one use case: downloading captions from your own channel. If you are building anything that reads other people's videos - a research tool, a RAG pipeline, a summarizer, a content analyser - the official API cannot do it. There is no paid tier that unlocks this. It is a permissions model, not a pricing gate.

The part that trips people up

A third endpoint, videos.list with part=contentDetails, returns a field called caption whose value is the string "true" or "false" rather than a boolean - a small detail that has caused a lot of quietly wrong if statements. It costs 1 quota unit and needs only an API key.

So with a plain API key you can cheaply check whether a video has captions, at scale, for free. You just cannot read them. People discover this field, assume it is the beginning of a working pipeline, and only later hit the 403 wall.

Worth knowing if you are budgeting quota: the free tier is 10,000 units/day. At 1 unit per call and 50 video IDs per call, videos.list covers 500,000 videos a day. captions.list at 50 units covers 200. The cheap endpoint is the one that does not give you text.

Option 1: youtube-transcript-api (Python)

The youtube-transcript-api library is where nearly everyone starts. It does not use the Data API at all - it reads the same caption data YouTube's web player uses.

python
from youtube_transcript_api import YouTubeTranscriptApi
YouTubeTranscriptApi.get_transcript("dQw4w9WgXcQ")

Free, open source, well maintained, and three lines. Locally it is excellent.

Where it breaks: YouTube blocks datacenter IP ranges. The moment you deploy to AWS, GCP, Vercel or any cloud host, calls start returning RequestBlocked or IpBlocked. This is not a bug and the maintainers document it - the recommended fix is routing through rotating residential proxies.

Which means the free option now costs a proxy subscription plus retry logic plus ongoing maintenance as YouTube's defences change. Many teams are fine with that. Many discover it the week after launch.

Use it when: prototyping locally, running from a residential connection, or fetching small volumes where a manual retry is acceptable.

Skip it when: it runs in the cloud, or a failed fetch costs more than a few cents.

Option 2: yt-dlp plus your own speech-to-text

If a video has no caption track at all, no caption API can help - the text does not exist yet and has to be generated.

The DIY version: yt-dlp to pull the audio, then Whisper (locally or via an API) to transcribe it.

bash
yt-dlp -x --audio-format mp3 -o audio.mp3 "https://youtube.com/watch?v=VIDEO_ID"
whisper audio.mp3 --model small --output_format json

Full control, no vendor, and it works on anything yt-dlp supports.

The costs are real though: you are downloading full media rather than a text file, running inference or paying per audio minute, and inheriting the same IP-blocking problem for the download step. For a handful of videos it is fine. For thousands it becomes infrastructure.

Use it when: you need transcripts for videos with no captions, you have compute, and you want no third-party dependency.

Skip it when: most of your videos already have captions - you would be paying speech-recognition prices for text that already exists as a file.

Option 3: a hosted transcript API

The category exists because of the gap described at the top: captions exist, and there is no sanctioned way for a third party to read them. Hosted services absorb the blocking, proxying and retry problem and return text.

The trade-off is straightforward - you pay per request instead of maintaining proxy infrastructure, and you depend on someone else's uptime. Judge them on four things:

Does it handle blocks for you? This is the entire value proposition. If you still need proxies, you have bought nothing.

What happens when a video has no captions? Some services return an error. Some fall back to speech recognition automatically. That difference decides whether your pipeline needs a second system bolted on.

Which platforms? Several are YouTube-only. If you also need TikTok, Instagram, X or Facebook, check before you build - retrofitting a second vendor is worse than picking one that covers everything up front.

How does pricing behave on failure? Ask whether failed requests are billed. At scale the difference compounds.

Current options include TranscriptFetch (ours), Supadata, TranscriptAPI and FetchTranscript, among others. We wrote a detailed comparison of the main YouTube transcript APIs with current pricing rather than repeating it here.

Choosing

Your situationUse
Prototyping locally, small volumeyoutube-transcript-api
Only your own channel's videosOfficial Data API + OAuth
Production, cloud-hosted, captions existHosted transcript API
Videos with no captions at allSpeech-to-text, DIY or via an API that falls back
Multiple platforms, one pipelineHosted API covering all of them

What "reliability" actually means here

Most comparisons of this problem talk about speed. In practice, the failure modes that matter are:

IP blocking - the dominant failure in production, and the one the official API does not have (because it does not work for third-party videos at all) and the Python library has badly.

No caption track - roughly independent of your tooling. Some videos simply have none, and only speech recognition fixes that.

Caption exists but is not retrievable - the gap this article opened with. YouTube reports a track through contentDetails.caption that no third party can read through official channels.

That last one is the least-documented and, in our view, the most interesting. We are currently running a pre-registered measurement study sampling YouTube at random to quantify how often it happens. When the numbers are in, we will publish them with the method and the dataset, whatever they say.

The short version

The official YouTube Data API cannot download captions for videos you do not own - captions.download requires the owner's OAuth token and returns 403 otherwise. That is a permissions rule, not a pricing tier, and no amount of quota changes it. Everything else in this space is a way of working around that fact: the Python library reads the player's data and gets IP-blocked in the cloud, speech-to-text regenerates text that already exists, and hosted APIs sell you the blocking problem as a service.

Start with youtube-transcript-api locally. When it stops working in production - and it will - you will already know why.