Fixing TranscriptsDisabled from youtube-transcript-api

Your script works for a hundred videos, then dies on one:

TEXT
youtube_transcript_api._errors.TranscriptsDisabled:
Subtitles are disabled for this video

If you're here from a stack trace, the short version: this is not a bug, a rate limit, or an IP block, and retrying will never fix it. Here's what's actually happening and what your options are.

What the error means

youtube-transcript-api does exactly one thing: it reads caption tracks that already exist on a video. It doesn't do any speech recognition of its own.

TranscriptsDisabled means the video has no caption track to read — either the uploader turned captions off, or YouTube never auto-generated them (common for music, very short clips, low-resource languages, and some region-locked uploads).

So the library is telling you the truth: there is nothing to fetch.

It's worth separating the errors people conflate:

ErrorCauseRetrying helps?
TranscriptsDisabledNo caption track exists at allNo — nothing to fetch
NoTranscriptFoundCaptions exist, but not in the language you asked forSometimes — try other languages
IpBlocked / RequestBlockedYouTube blocked your IP (common on cloud hosts)Yes — with a residential proxy

What doesn't fix it

Retries and backoff. The response isn't flaky; it's a definitive "no captions here." Retrying just burns time.

Proxies. Residential proxies are genuinely necessary for a different problem — YouTube blocks datacenter IPs, so the same code that works on your laptop can fail on AWS or Vercel with IpBlocked. But a proxy changes who is asking, not whether the captions exist. It will not fix TranscriptsDisabled.

Trying every language. Worth a shot for NoTranscriptFound, useless here:

Python
from youtube_transcript_api import YouTubeTranscriptApi

api = YouTubeTranscriptApi()
# Listing shows you what actually exists — for a TranscriptsDisabled video,
# this raises too, because there is no track list at all.
transcripts = api.list(video_id)

The only real fix: transcribe the audio

If there's no caption track, the only way to get text is to listen to the video. That means: pull the audio, run speech recognition, and format the result.

Doing it yourself

Fully workable, and worth knowing even if you outsource it later:

Shell
# 1. Pull audio only
yt-dlp -f bestaudio -x --audio-format mp3 -o audio.mp3 "https://youtu.be/VIDEO_ID"
Python
# 2. Transcribe it
from openai import OpenAI

client = OpenAI()
with open("audio.mp3", "rb") as f:
    result = client.audio.transcriptions.create(
        model="whisper-1",
        file=f,
        response_format="verbose_json",  # needed for per-segment timestamps
    )

for seg in result.segments:
    print(seg.start, seg.text)

Two details that bite people:

  • Ask for verbose_json if you want timestamps. The default response is a flat string, and some newer transcription models won't return segment timestamps at all — whisper-1 with verbose_json will.
  • Mind the file size limit. Long videos need to be chunked (or compressed) before upload, and chunk boundaries have to be stitched back together with their offsets or your timestamps drift.

What it costs you to run

The DIY path is the right call if transcription is your core product. If it isn't, the ongoing costs are: residential proxies (for the caption path and for downloads from cloud IPs), audio storage and cleanup, a queue and workers for jobs that take minutes, chunking and stitching for long videos, retries when YouTube changes something, and a cache so you don't pay twice for the same video.

That's a small service, not a function.

Doing it with an API

This is the problem transcript.land exists for. One call returns captions when they exist and falls back to AI transcription when they don't — the response shape is identical either way, so your code stops caring which path ran:

Shell
curl -X POST https://api.transcript.land/v1/transcript \
  -H "Authorization: Bearer $TRANSCRIPT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url": "https://youtu.be/VIDEO_ID", "format": "json"}'

Or from the terminal:

Shell
brew install ziqorg/tap/transcript
transcript login
transcript get "https://youtu.be/VIDEO_ID"

Videos with captions come back in under a second. Videos without return a job_id you poll while the audio is transcribed. The same works for TikTok, X, Instagram, Bilibili, Facebook, and RedNote, and you can export to TXT, SRT, VTT, Markdown, or JSON. There's a free tier if you just need to unblock a handful of videos.

Summary

  • TranscriptsDisabled means no caption track exists — retries and proxies can't help, because there's nothing to fetch.
  • Proxies solve a different error (IpBlocked), which is real but separate.
  • The only fix is transcribing the audio: yt-dlp + a speech model yourself, or an API that falls back automatically.

If you want to skip the pipeline, try it on the video that broke your script — paste the URL and see whether it comes back.