The direct answer: for local scripts, notebooks, prototypes, and one-off pulls, use the free youtube-transcript-api library — in this test it delivered a transcript for 7 of the 8 videos that had one, at zero cost. Use the managed YouTube Transcript Scraper when the same job runs unattended from cloud infrastructure, where the library's documented IP-block failure mode applies and per-item billing semantics start to matter. Neither tool reads captions that don't exist; both failed the same three undeliverable inputs.
The test: same 11 videos, both tools, one day
On 2026-07-17 we ran the same fixed sample of 11 video IDs through both tools: 8 well-known public videos expected to carry captions (including one Short and one non-English video), one live-stream URL, and two syntactically valid but nonexistent IDs. The library ran locally (version 1.2.4, Python 3.9.6, macOS, residential ISP connection, default fetch() call). The actor ran as one 11-ID batch on the Apify platform. Full environment details are in the methodology section.
| Video | youtube-transcript-api 1.2.4 | YouTube Transcript Scraper | Charge event, at list price |
|---|---|---|---|
jNQXAC9IVRw“Me at the zoo” (2005) | English manual, 6 segments | English manual, 6 segments | $0.0030 |
dQw4w9WgXcQRick Astley — Never Gonna Give You Up | English manual, 61 segments | English manual, 61 segments | $0.0030 |
kJQP7kiw5FkLuis Fonsi — Despacito | English manual, 79 segments | English manual, 90 segments | $0.0030 |
9bZkp7q19f0PSY — Gangnam Style | NoTranscriptFound exception | Korean auto-generated, 67 segments | $0.0030 |
fJ9rUzIMcZQQueen — Bohemian Rhapsody | English manual, 74 segments | English manual, 74 segments | $0.0030 |
hTWKbfoikegNirvana — Smells Like Teen Spirit | English manual, 53 segments | English manual, 53 segments | $0.0030 |
M7lc1UVf-VEYouTube Developers — Player API demo | English manual, 466 segments | English manual, 466 segments | $0.0030 |
LgbyEFILLJIMrBeast Short | English auto-generated, 28 segments | English auto-generated, 28 segments | $0.0030 |
jfKfPfyJRdklofi girl — live radio stream | VideoUnplayable exception | success:false, code PLAYABILITY | $0.0000 |
00000000000nonexistent ID | VideoUnavailable exception | success:false, code PLAYABILITY | $0.0000 |
aaaaaaaaaaanonexistent ID | VideoUnavailable exception | success:false, code PLAYABILITY | $0.0000 |
Totals: the library returned 7 transcripts and raised 4 exceptions, for $0. The actor delivered 8 transcripts and returned 3 unbilled success: false rows in the same dataset; the batch finished in 21 seconds with run status SUCCEEDED. The run emitted eight transcript charge events and one start event, corresponding to $0.02405 at public list prices (8 × $0.003 plus a $0.00005 start fee). This was an owner-account run, so that figure is the list-price value of the emitted events, not an observed customer charge.
The full evidence — normalized results from both tools, the actor input, the run metadata, and the library test script (JSON) — is downloadable, with transcript text omitted because caption text can be copyrighted.
What the test shows
- On unambiguous inputs the two tools return the same data. Seven videos produced the same track, language, and (with one parser difference on Despacito: 79 vs 90 segment boundaries) the same content both ways. The library is not a lesser tool for reading captions.
- The one delivery difference is default language behavior, not capability. The library's default
fetch()requests English and raisedNoTranscriptFoundon Gangnam Style, which carries only Korean tracks; passinglanguages=["ko"]would have fetched it. The actor's default falls back across available tracks and delivered the Korean track unprompted. If your pipeline handles arbitrary videos in unknown languages, defaults matter; if you control the inputs, they don't. - Failures surface differently: exceptions vs rows. The library raises well-typed exception classes (
VideoUnavailable,VideoUnplayable,NoTranscriptFound) — good to handle in a script you babysit, but each one is a control-flow event your batch loop must catch. The actor writes an unbilledsuccess: falserow with a stable machine-readable code and keeps the batch going; the run itself still succeeds. - Wall-clock was comparable. Sequential library calls took ~33 seconds for the 11 IDs; the actor batch took 21 seconds. Neither result generalizes beyond this sample size.
What this test did not measure
The best-known difference between the two tools is deliberately absent from the table above: the library ran from a residential connection, where it works. Deployed to a datacenter IP (AWS, GCP, Hetzner, most CI runners), it commonly raises RequestBlocked / IpBlocked — a failure mode documented in the library's own README, which recommends rotating residential proxies to work around it. We did not reproduce it in this test, and our write-up of that failure mode and its fixes is a separate guide. The actor runs on managed infrastructure where proxy handling is included in the per-result price.
The decision, by dimension
| Dimension | youtube-transcript-api | YouTube Transcript Scraper |
|---|---|---|
| Cost | Free (MIT-licensed; proxy costs in cloud deployments are documented in its README) | $3.00 per 1,000 delivered transcripts; failed items $0 (list pricing; charge events observed first-hand) |
| Setup | pip install youtube-transcript-api (first-hand, this test) | Apify account (free tier works) + one API call (first-hand, this test) |
| Runs from datacenter IPs | Commonly blocked; README recommends rotating residential proxies (tested by us only from a residential IP) | Yes — proxy handling is the product (first-hand, this run) |
| One bad video in a batch | Raises an exception your loop must catch (first-hand) | Unbilled success:false row; batch continues (first-hand) |
| Default language handling | Requests English unless languages is passed (first-hand) | Falls back across available tracks (first-hand) |
| Maintenance when YouTube changes | You update the dependency and redeploy (by design) | Absorbed by the operator (apihq's own claim) |
| Privacy of inputs | Requests go directly from your machine to YouTube (by design) | Requests flow through Apify and apihq infrastructure (by design) |
| Rate/spend control | Yours to build (by design) | Apify Run Limits cap spend per run (documented on the actor's pricing section; not exercised in this test) |
When the library is the better choice
- Local scripts, notebooks, and prototyping — no reason to pay.
- Low volume from a residential connection.
- Zero-budget projects where you can absorb blocks and retries yourself.
- When requests must not transit third-party infrastructure.
When the actor is the better choice
- Unattended pipelines on cloud infrastructure, where the IP-block failure mode applies to direct library use.
- Batches where one bad input must not break the job — the typed, unbilled failure row is the contract.
- When you'd otherwise buy and rotate residential proxies: compare that line item against $3.00 per 1,000 results.
Working code for both tools, including the actor call from Python, is in the Python guide.
Methodology
- Sample: the 11 video IDs listed in the results table, chosen by hand before the test to cover captioned videos (old and new, English and not, long-form and Short), one live stream, and two nonexistent IDs. The sample is small and skews toward famous, well-maintained videos; it supports the behavioral comparison above and no availability-rate claims about YouTube at large.
- Library run: youtube-transcript-api 1.2.4, Python 3.9.6, macOS, residential ISP connection, 2026-07-17. Default
YouTubeTranscriptApi().fetch(video_id)per ID, sequential, no proxy configuration. - Actor run: one 11-ID batch submitted to
apihq/youtube-transcript-scraper(build 0.1.7) via Apify's run-sync API on 2026-07-17. Run status SUCCEEDED in 21 seconds; the run'schargedEventCountsshows 8transcriptevents plus 1 start event — $0.02405 at public list prices. The run was made from the operator's own account, so no customer charge was observed; a non-owner run would be billed those events at the listed prices. - Evidence package: youtube-transcript-comparison-2026-07-17.json holds the exact actor input, normalized per-video results from both tools, the actor run's metadata (run ID, build, timestamps, charge events, list-price calculation), and the Python script the library rows came from. Transcript text is omitted; outcome, language, track type, and segment counts are what the claims rest on.
- Affiliation:apihq operates the YouTube Transcript Scraper. The library rows report the library's actual observed behavior; where we did not test something (datacenter blocking), the table says so and links the primary source.
- Corrections: if a row above is wrong or has gone stale, use the support and corrections route and it will be re-tested and corrected with a dated note.