youtube-transcript-api blocked: what the errors mean and how to fix them

The traceback ends in Could not retrieve a transcript for the video, the line after it says RequestBlocked or IpBlocked, and the same script ran fine at your desk an hour ago. Here is what each error is telling you, why it only happens on the server, and how to make it stop.

The short answer

Your server's IP is blocked by YouTube, and no code change unblocks it. Fetch the transcript through TranscriptAPI instead: same video ID, same segments, and the blocking happens on our side, not yours. Get 20 free credits, no card.

curl
curl "https://api.transcriptapi.io/transcript?video_id=dQw4w9WgXcQ" \
  -H "Authorization: Bearer ta_..."

The exact errors the library raises

Every failure in youtube-transcript-api subclasses CouldNotRetrieveTranscript, whose message is Could not retrieve a transcript for the video {url}! followed by the specific cause. The cause line is the part that matters. Names and messages below are from the library's _errors.py.

ExceptionMessage (start of)What it meansRetry?
RequestBlockedYouTube is blocking requests from your IP.The IP you fetch from is on a list YouTube treats as a bot source. Nearly always a cloud provider range.Not from this IP. Change the exit address.
IpBlockedYouTube is blocking your requests, despite you using proxies.You configured a proxy and the proxy's exit IP is flagged too. Common with datacenter proxies and cheap shared residential pools.Rotate to a different exit; back off first.
TranscriptsDisabledSubtitles are disabled for this videoThe uploader turned captions off. There is no track to read.Never. Skip the video.
NoTranscriptFoundNo transcripts were found for any of the requested language codesCaptions exist, but not in the languages you asked for. The message lists what is available.Only with a different language list.
VideoUnavailableThe video is no longer availablePrivate, deleted, or region-blocked from where the request went out.Never, unless the region is the cause.
AgeRestrictedThis video is age-restricted. Therefore, you are unable to retrieve transcripts for it without authenticatingYouTube wants a signed-in adult account before it serves the page.Never without cookies from a logged-in session.

Two more show up less often. VideoUnplayable carries YouTube's own reason string. PoTokenRequired means YouTube asked for a proof-of-origin token that the library cannot mint, which is another way of saying the request looked automated. Treat it like a block.

Split the table in two

The first two rows are about your IP. The other four are about the video. Retrying a video-side error burns proxy bandwidth and changes nothing. TranscriptAPI flags each error as retryable or not, so you never have to guess.

Why it works on your laptop and fails on the server

The library reads the caption track through the same watch page your browser loads. YouTube serves that page freely to residential addresses and refuses it to address ranges it associates with bots, which in practice means every cloud provider. Your laptop is on a home ISP. Your container is on AWS, GCP, Azure, Hetzner, Fly, Railway or Render. The code is identical; the address is not.

The README is explicit: YouTube "has started blocking most IPs that are known to belong to cloud providers ... which means you will most likely run into RequestBlocked or IpBlocked exceptions when deploying your code to any cloud solutions."

A second trap: curl from the server returning a 200 for the watch URL. That does not prove the caption endpoint is reachable; the block applies to the caption fetch, not the HTML shell. The false positive, and why the official Data API is not a way around it, is covered in why you cannot download captions you do not own.

A third: exits in the EU intermittently get the cookie-consent wall instead of the page, which the library reports as a failed request. It comes and goes per IP, so it reads as flakiness.

The fix: move the fetch to a hosted API

Keeping the library working on a server is possible, but it turns into its own project:

  • Rotating residential proxies. Datacenter proxies get blocked the same way your server does.
  • A pool pinned to one non-EU region, or the cookie-consent wall shows up as random failures.
  • Retries through fresh exit IPs, capped. A retry through the same blocked address can't succeed.
  • Health checks that fetch a real transcript, because a 200 on the watch page proves nothing.
  • Metered bandwidth of about 377 KB per fetch, paid again for every failed attempt.
  • Silent breakage whenever YouTube changes its pages, until a new library version ships.

TranscriptAPI already runs all of that. You send a video ID and get segments back in the same text / start / duration shape the library returns. Blocks are handled on our side, failed requests are refunded, and a video you have fetched before comes from cache for free.

One credit per transcript. You get 20 free credits at signup, then from $10 for 2,500, or $49 a month for 25,000 (see the pricing page). It can't read a video whose captions are turned off; nothing can. What goes away is the whole blocked class of failures. The full comparison is on the youtube-transcript-api alternative page.

How TranscriptAPI maps these failures

We run the same class of fetch, so we see the same exceptions. Each one is translated into a status code, an error name, a plain-language hint, and a retryable flag you can branch on without parsing prose. The credit is refunded in every case.

Library exceptionStatusretryablehint
TranscriptsDisabled404falseRetrying will not help — the uploader has disabled captions for this video.
NoTranscriptFound404falseOmit the language parameter to get whatever language the video has.
VideoUnavailable404falseCheck that the video is public.
AgeRestricted404falseRetrying will not help.
RequestBlocked / IpBlocked503trueTemporary. Retry in a few seconds; a fresh request goes out through a different route.
anything else upstream502trueUsually temporary. Retry in a few seconds.
404 — video-side, refunded
{
  "detail": "This video has captions turned off, so there is no transcript to fetch. Your credits have been refunded.",
  "error": "TranscriptsDisabled",
  "hint": "Retrying will not help — the uploader has disabled captions for this video.",
  "retryable": false,
  "credits_refunded": true
}

The rule for an automation is one line: 404 means skip, 5xx means retry with backoff. Status codes and the refund policy are documented in the API reference.

The same fetch, hosted

curl
curl "https://api.transcriptapi.io/transcript?video_id=dQw4w9WgXcQ" \
  -H "Authorization: Bearer ta_..."
python
import requests

r = requests.get(
    "https://api.transcriptapi.io/transcript",
    params={"video_id": "dQw4w9WgXcQ"},
    headers={"Authorization": "Bearer ta_..."},
    timeout=60,
)
if r.status_code == 404:
    skip(r.json()["hint"])     # retryable: false
elif r.status_code >= 500:
    retry_later()               # refunded already
else:
    segments = r.json()["transcript"]

Porting an existing codebase, including the older get_transcript() form, is walked through in the migration guide.

Test it on the video that is failing

Sign in for 20 free credits, no card, and run the failing ID through /transcript. If it comes back 404, the video was never readable and the proxy was not your problem. If it comes back 200, it was.

Frequently asked questions

Why does youtube-transcript-api say RequestBlocked on my server but not on my laptop?

Your laptop sits on a residential IP; the server sits on a cloud provider range that YouTube treats as a bot source. Same code, same video, different address. The library's README states that deployments to AWS, GCP, Azure and similar will most likely hit RequestBlocked or IpBlocked.

Does a VPN or a datacenter proxy fix IpBlocked?

Usually not for long. Datacenter proxy ranges are just as well known to YouTube as the cloud ranges you are already on. Only rotating residential proxies get through reliably, and they are billed per gigabyte and need ongoing tuning. TranscriptAPI runs that pool for you: one API key, no proxy account.

Is 'Could not retrieve a transcript for the video' the same as being blocked?

No. It is the base class message every failure shares. The line after it names the real cause: RequestBlocked or IpBlocked means your IP, TranscriptsDisabled or NoTranscriptFound means the video has no usable captions, VideoUnavailable or AgeRestricted means you cannot reach the page at all. Read the second line before you retry.

Should I retry TranscriptsDisabled or VideoUnavailable?

No. Those describe the video, not the connection, and a retry returns the same answer while spending proxy bandwidth. TranscriptAPI marks every error with retryable: true or false, so your code knows which is which, and refunds the credit either way.

How does TranscriptAPI handle the same failures?

Every failure is returned with an error name, a plain-language hint, and retryable: true or false. Video-side problems are a 404 with retryable: false; a block on our side is a 503 with retryable: true. The credit is refunded in both cases. Retry the 503s with backoff and skip the 404s.

Can TranscriptAPI read age-restricted or private videos?

No. Nobody can without the owner's account. Those come back as a 404 with the reason in the hint, refunded. What we remove is the blocking class of failures: the proxies, the retries and the format changes are on our side.