RequestBlocked in Google Colab: why notebooks get blocked most, and what to do instead
Colab is where most people meet this error, and it is the worst place to debug it: the environment is disposable, the fix everyone reaches for is a coin flip, and the standard remedy involves putting a password into a document you are about to share.
The 30-second version
- Colab is a cloud IP, so it is in scope for the block. The library's README names Google Cloud Platform explicitly among the providers whose IPs YouTube "has started blocking". Issue #585 reports exactly this:
RequestBlockedwhen running in Google Colab. - Colab gets hit harder than a private server. This is where the tutorials get run. Any address you land on has plausibly already been used to fetch transcripts today by someone following the same tutorial.
- "Restart the runtime" is a coin flip, not a fix. It sometimes works, which is precisely why people build a whole notebook on top of it and then watch it fail during a demo.
- The proxy remedy has a Colab-specific hazard. Credentials typed into a cell are saved in the
.ipynband travel with the notebook when you share it. Use Colab Secrets. - The environment is disposable, so cache off-machine. Colab VMs "are deleted when idle for a while, and have a maximum lifetime enforced by the Colab service". Every deletion is another round of refetching, which is another round of exposure to the block.
Why notebooks get blocked more than servers do
A production server on Render or Railway is blocked because it is a datacenter IP with an automated traffic profile. Colab is that plus a compounding factor: it is the default place people run example code.
Search results, YouTube tutorials and LLM answers all converge on the same five lines of youtube-transcript-api, and a large share of the people running them run them in a free Colab notebook. Colab allocates you a runtime from a shared pool, so the address you receive has a history you did not create. You can be blocked on your first cell execution, before you have made a single request, because the previous occupant of that address spent the afternoon pulling a thousand transcripts.
This is why the experience is so confusing: the exact code from a working tutorial fails immediately, with no signal that anything is wrong with the code. Nothing is wrong with the code.
Why "restart the runtime" is the wrong lesson
Restarting or reconnecting can land you on a different runtime with a different address, and sometimes that address is clean. So the notebook that just failed now succeeds, and the natural conclusion is that restarting fixes it.
What you have actually learned is that the outcome is random. That is a much worse position than a deterministic failure, because:
- It works while you are testing and fails when you present it.
- It works for the first 30 videos of a batch and fails at 31, leaving you with a half-populated dataframe and no record of what is missing.
- It teaches you to respond to a systematic problem by clicking a button, which does not scale past a handful of videos.
If you are going to depend on the retry-with-a-new-runtime behaviour at all, at least make it explicit and bounded, and make sure the retry actually opens a new connection rather than reusing a pooled one — details in the RequestBlocked / IpBlocked guide.
The Colab-specific hazard: credentials in a shared notebook
The standard remedy is a rotating residential proxy. In a script on your own machine you put the credentials in an environment variable and forget about it. In Colab that instinct is a genuine security problem.
A notebook cell's source is saved into the .ipynb file. If you write your Webshare username and password into a cell, they are now in the document — and notebooks are shared constantly, committed to GitHub, exported to PDF and handed round as course materials. A proxy subscription leaked this way is billed by the gigabyte to whoever finds it.
Colab has a built-in answer. Use the Secrets panel (the key icon in the left sidebar), store the values there, and read them at runtime:
from google.colab import userdata
PROXY_USER = userdata.get("WEBSHARE_USER")
PROXY_PASS = userdata.get("WEBSHARE_PASS")
Secrets live in your Google account, not in the notebook file, and a copy of the notebook shared with someone else does not carry them. The cell above is safe to publish; the same two values typed as literals are not.
The same applies to any API token you use from a notebook. Two lines, and it removes an entire class of accident.
The other Colab constraint people forget: the machine goes away
Colab's own FAQ is direct about this: "Virtual machines are deleted when idle for a while, and have a maximum lifetime enforced by the Colab service", "runtimes will time out if you are idle", and in the free tier "notebooks can run for at most 12 hours". It also warns that "Colab resources are not guaranteed and not unlimited".
For transcript work that has a specific and expensive consequence: anything you wrote to the local filesystem is gone, so tomorrow you fetch it all again. Every refetch is another set of requests that can be blocked, for data you already successfully retrieved once. People end up blaming the library for a problem they created by not persisting anything.
Mount Drive and cache there. This is the highest-value change in the whole notebook:
import json, os
from google.colab import drive
drive.mount("/content/drive")
CACHE = "/content/drive/MyDrive/transcript_cache"
os.makedirs(CACHE, exist_ok=True)
def cached(video_id, lang, fetch):
path = os.path.join(CACHE, f"{video_id}.{lang}.json")
if os.path.exists(path):
with open(path) as f:
return json.load(f)
data = fetch(video_id, lang) # the part that can get blocked
with open(path, "w") as f:
json.dump(data, f)
return data
Cache failures too, with the error code. A video with captions disabled will still have them disabled next week; asking again every session is pure exposure for a guaranteed answer. Storing outcomes rather than only successes is what turns a flaky notebook into a resumable one — rerun the cell and it picks up exactly where it stopped instead of starting over.
If you are doing research, work in two passes
Most Colab transcript work is analysis: a corpus of videos, then some counting, coding or modelling over the text. Those two halves have completely different reliability profiles, and putting them in one loop is what makes a notebook fragile.
- Acquisition pass. One cell whose only job is to get text and write it to Drive, designed to be run repeatedly and to skip everything it already has. Let it fail on some videos; run it again.
- Analysis pass. Reads only from Drive, never touches the network, and therefore always runs to completion — reproducibly, which also matters if the work is going into a paper.
This also protects your results: a corpus assembled over several sessions is a fixed artifact you can cite and re-analyse, rather than something that silently differs each time you run the notebook. More on building a defensible corpus in transcripts for research.
The route that skips the address problem
Because Colab is disposable and shared, the case for not owning the retrieval stack is stronger here than anywhere else. There is no server to configure, no proxy subscription to protect, and nothing to keep current between sessions — one HTTPS call, with the token in Colab Secrets:
import requests
from google.colab import userdata
TOKEN = userdata.get("VIDWORDS_TOKEN")
def fetch(video_ids, lang="en"):
resp = requests.post(
"https://vidwords.com/api/transcripts",
headers={"Authorization": f"Basic {TOKEN}"},
json={"ids": video_ids, "lang": lang},
timeout=60,
)
resp.raise_for_status()
return resp.json()["results"]
for video in fetch(["dQw4w9WgXcQ", "9bZkp7q19f0"]):
if "error" in video:
print(video["id"], "->", video["error"]) # no_transcript, video_unavailable, invalid_id
continue
print(video["title"])
print(video["text"][:200], "...")
Two details that matter in a notebook. Failures come back per video as an error field rather than raising, so one bad id does not abort a cell you have been running for ten minutes. And you can send up to 50 ids in one request, so a 200-video corpus is four calls rather than 200 opportunities to be blocked.
One delivered transcript costs one credit; a video with no transcript costs nothing. A free account includes 25 transcripts a month with no card, which is enough to test the notebook end to end before deciding anything. Everything else is in the API reference. Wrap it in the Drive cache above and the notebook becomes resumable as well as reliable.
Which route to pick
- A handful of videos, run by hand: use the open-source library, restart the runtime when it sulks, and do not build anything. It is free and it is fine.
- A corpus you care about: cache to Drive first, whatever else you decide. Then pick a retrieval route. The cache is what makes either one survive a deleted VM.
- A notebook you will share or teach from: keep every credential in Colab Secrets, and prefer the route with the fewest moving parts, because you will not be there when someone else runs it.
- Not code at all? If you just need the text of a few videos, paste the link on the VidWords homepage and copy the result — three a month with no account at all, 25 with a free one.
FAQ
Does Colab Pro fix the block?
It changes your compute allocation, not YouTube's opinion of the address you are given. There is no tier that buys a residential IP.
Can I run a proxy inside the notebook?
You can configure the library to use one — that is the supported remedy. Keep the credentials in Colab Secrets rather than in a cell, and be aware the README states plainly that a proxy "doesn't guarantee that you won't be blocked".
Why does the first cell work and the tenth fail?
You crossed a threshold on an address whose recent history you did not control. It is not your tenth request that mattered — it is the total from that address.
The video genuinely has no captions. Now what?
No proxy helps; there is nothing to download. Getting text out of that video means transcribing its audio, which is a different and more expensive operation — see why a transcript can be missing.