YouTube Transcript & Subtitles Extractor - Text, SRT, VTT
Pricing
from $4.00 / 1,000 transcript fetcheds
YouTube Transcript & Subtitles Extractor - Text, SRT, VTT
Extract YouTube transcripts and subtitles in bulk. Returns plain text, timestamped segments, SRT, VTT, or LLM-ready chunks. Handles auto-generated and human captions in any language. Pay only for videos that return a transcript.
Pricing
from $4.00 / 1,000 transcript fetcheds
Rating
0.0
(0)
Developer
HJL Analytics and AI development
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
YouTube Subtitles & Captions Scraper — Text, SRT, VTT
Download the subtitles of any YouTube video, in bulk, as plain text, timestamped segments, SRT, WebVTT, or chunks ready to feed a language model.
Paste video URLs, get subtitles back. No API key, no browser, no OAuth. You are only charged for videos that actually return a transcript.
What it does
- Download YouTube subtitles in bulk — hundreds of videos in one run
- Save YouTube captions as an SRT or VTT subtitle file
- Get the subtitle track in any language, auto-generated or human-written
- Extract a YouTube transcript as plain text for summarising, searching, or translating
- Convert a YouTube video to text without downloading the video itself
- Prepare subtitles for RAG and LLM pipelines, chunked with timestamps preserved
- Accepts watch links,
youtu.beshort links, Shorts, embeds, live URLs, or bare video IDs
Works with every subtitle track YouTube exposes: manually written captions, auto-generated ones, and translated subtitle tracks in any of the languages a video offers.
Output
One row per video. A 19-minute talk looks like this:
{"videoId": "aircAruvnKk","status": "ok","title": "But what is a neural network? | Deep learning chapter 1","channel": "3Blue1Brown","durationSeconds": 1120,"viewCount": 23919789,"language": "en","isGenerated": false,"languageMatch": "exact-human","cueCount": 286,"wordCount": 3357,"text": "This is a 3. It's sloppily written and rendered at an extremely low resolution...","chunks": [{ "index": 0, "text": "This is a 3...", "startSeconds": 4.22, "endSeconds": 71.5, "estimatedTokens": 291 }]}
Input
| Field | Description |
|---|---|
videoUrls | Video URLs or 11-character IDs. Duplicates are removed, so the same video pasted twice is charged once. |
preferredLanguages | Language codes in order, e.g. ["nl", "en"]. Defaults to ["en"]. |
outputFormats | Any of text, segments, chunks, srt, vtt. Only what you ask for is returned. |
chunkTargetChars | Target chunk size when using chunks. Default 1200. |
chunkOverlapChars | Context repeated at each chunk start. Default 100. |
includeAvailableLanguages | List every caption language the video offers. |
useResidentialProxy | On by default and effectively required — see the note below. |
proxyCountry | Optional two-letter country code, e.g. US or NL. |
maxVideos | Safety cap. 0 means no limit. |
concurrency | Videos fetched at once. Default 5. |
How language selection works
Getting the wrong track is the usual reason a transcript is useless, so the order is explicit:
- Your requested language, human-written
- Your requested language, auto-generated
- A regional variant of it —
en-GBwhen you asked foren - Any human-written track
- Anything available
Each result reports which rule applied in languageMatch, so you can tell a precise hit from a fallback rather than guessing.
Human-written beats auto-generated at the same language on purpose: machine captions carry no punctuation and mangle proper nouns, which is exactly what makes a transcript useless further down a pipeline.
LLM-ready chunks
Selecting chunks merges caption fragments into passages of roughly your target size, breaking at sentence ends where possible and keeping the timestamp each passage started at.
That timestamp is the point. It means an answer generated from a chunk can be traced back to a moment in the video, instead of being an unattributable claim. Each chunk also carries a token estimate so you can budget a model call before making it.
When a video fails
Failures are rows, not crashes. One private video in a batch of 500 leaves you with 499 transcripts and one explained failure — and you are not billed for the failure.
errorCode | What it means | Retried |
|---|---|---|
NO_CAPTIONS | The video has no caption tracks, or is unavailable or deleted | no |
LOGIN_REQUIRED | The video is private or age-restricted and needs an account | no |
BOT_CHECK | YouTube challenged the address the request came from | yes |
NETWORK_ERROR | The request never reached YouTube | yes |
TIMEOUT | YouTube did not answer in time | yes |
CAPTIONS_GATED | Captions exist but sit behind YouTube's proof-of-origin gate | no |
EMPTY_RESPONSE | YouTube accepted the request and returned nothing, which means the same gate | no |
EMPTY_TRANSCRIPT | The track downloaded but contained no text | no |
UNREADABLE_INPUT | The input was not a YouTube video URL or ID | no |
Retryable failures are attempted again from a different proxy address, up to maxRetries times. Permanent ones are not, so a private video never wastes your time or money on retries.
BOT_CHECK and LOGIN_REQUIRED are worth distinguishing because YouTube reports both with the same status code and only the message differs: one means the video needs an account, the other means the exit address was under suspicion. Conflating them fails videos that are perfectly fine.
Honest notes
Playlists and channels are not supported. Supply individual videos. A channel URL returns a clear message rather than a vague parse error.
This relies on YouTube's internal player API, which is undocumented and changes without notice. As of 2026 YouTube gates caption downloads behind a proof-of-origin token that ordinary requests cannot mint; this actor uses a client that is not subject to that gate, and falls back through several others when one stops working. If YouTube closes all of them, transcripts will fail and the actor will need updating — the run log says so explicitly rather than returning an empty result and leaving you to guess.
A residential proxy is required and is on by default. YouTube refuses datacenter address ranges outright, answering every request with "Sign in to confirm you're not a bot". This was measured, not assumed: from a datacenter connection zero of three caption-bearing videos returned a transcript, and the identical code through a residential exit returned all three. Each video uses its own proxy session so a large batch leaves from many addresses rather than concentrating behind one.
Auto-generated captions are approximate. They have no punctuation, guess at proper nouns, and struggle with accents and music. Every result flags isGenerated so you can decide whether that matters for your use.
Local development
npm installnpm test # 40 unit tests, no networknode cli.js https://youtu.be/aircAruvnKk --format text,chunksnode cli.js VIDEO_ID_1 VIDEO_ID_2 --lang nl,en
The CLI runs the same pipeline as the actor, so breakage can be diagnosed locally instead of by spending platform credit.