HomeBlog

yt-dlp Subtitles vs a Transcript API: Which One to Use

Split view of a yt-dlp subtitle download command next to a clean JSON transcript API response

yt-dlp can download subtitles for basically any YouTube video, and if you've used it for media downloads you've probably already discovered the flags. It's a legitimately great tool — I keep it installed everywhere. But "can download subtitles" and "is the right way to get transcripts in a service" are different claims, and I've been burned by conflating them. Here's what each approach actually looks like, including the cleanup step nobody warns you about.

The yt-dlp way, end to end

Grabbing auto-generated English subtitles without downloading the video:

yt-dlp --write-auto-sub --sub-lang en --skip-download \
  -o "%(id)s" "https://www.youtube.com/watch?v=VIDEO_ID"

(Use --write-sub instead for human-uploaded tracks, or both flags together.) This drops a VIDEO_ID.en.vtt file. Open it and you'll see why you're not done:

WEBVTT
Kind: captions
Language: en

00:00:00.320 --> 00:00:02.879
so today we're going to look at
00:00:01.760 --> 00:00:04.560
so today we're going to look at
how youtube handles

Auto-subs are "rolling" captions — each cue repeats the previous line, so naive stripping gives you every sentence twice. Everyone who goes down this road ends up writing some version of this cleanup:

import re, pathlib

lines = []
for line in pathlib.Path("VIDEO_ID.en.vtt").read_text().splitlines():
    if "-->" in line or line.startswith(("WEBVTT", "Kind:", "Language:")) or not line.strip():
        continue
    line = re.sub(r"<[^>]+>", "", line).strip()   # strip inline timing tags
    if not lines or line != lines[-1]:            # collapse rolling duplicates
        lines.append(line)
text = " ".join(lines)

Command, subprocess handling, file I/O, dedupe — call it 40 lines of glue before you have plain text.

Where yt-dlp is the right answer

Where it hurts on a server

Three separate maintenance burdens show up the moment yt-dlp moves into a backend:

The API way

The hosted-endpoint version of the entire pipeline above:

curl -s "https://youtube2text.org/api/transcribe?url=VIDEO_ID" \
  -H "x-api-key: yt_YOUR_KEY"

Returns JSON — {"result": {"videoId", "title", "pubDate", "content", ...}} — already deduplicated plain text, with structured errors (TRANSCRIPT_UNAVAILABLE when no captions exist, instead of stderr archaeology). A shared demo key is one call away (curl -s https://youtube2text.org/api/demo-key, 5 videos/month per IP); the full quickstart is in extracting transcripts with curl.

The honest footnote

Both approaches fetch the same captions from the same place, and both have to deal with YouTube not wanting automated traffic. yt-dlp makes that your problem — fine on a laptop, a proxy project on a server. A hosted API makes it the provider's problem: youtube2text.org routes fetches through rotating residential proxies server-side, which is precisely the piece you can't easily replicate from a datacenter IP. You're not paying for the transcript; you're paying to not operate that.

My split: yt-dlp for archives and one-offs on my own machine, the API for anything that runs unattended. If that matches your situation, keys are at youtube2text.org/app/keys — Google sign-in, free 5/month tier, paid from $5.99.