YouTube Transcript Scraper - Captions, SRT, VTT & RAG
Pricing
from $5.00 / 1,000 transcript extracteds
YouTube Transcript Scraper - Captions, SRT, VTT & RAG
Unofficial and not affiliated with YouTube or Google. Extract clean, timestamped transcripts from public videos and Shorts with language selection, translation, subtitles, Markdown, and RAG-ready chunks.
Pricing
from $5.00 / 1,000 transcript extracteds
Rating
0.0
(0)
Developer
Sebastián S
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
YouTube Transcript Scraper
Unofficial integration. This Actor is not affiliated with, endorsed by, or sponsored by YouTube or Google.
youtube-transcript-scraper is an HTTP-only Apify Actor for extracting captions from individual public YouTube videos. It validates every input before networking, selects caption tracks deterministically, and emits clean text, timestamped segments, SRT, WebVTT, Markdown, or segment-aware RAG chunks.
The Actor does not use a browser, download video or audio, sign in, transcribe audio, crawl playlists or channels, scrape comments, or generate AI summaries.
Input
{"videoUrls": ["aircAruvnKk", "https://youtu.be/aircAruvnKk"],"preferredLanguages": ["en-US", "en", "es"],"captionPreference": "manual-first","fallbackToAnyLanguage": true,"translateTo": "es","outputFormats": ["text", "segments", "srt", "vtt", "markdown", "chunks"],"cleaning": {"decodeHtmlEntities": true,"normalizeWhitespace": true,"normalizeUnicode": true,"deduplicateAdjacentSegments": true,"removeNonSpeechCues": false,"paragraphGapSeconds": 2.5},"chunking": {"maxCharacters": 2000,"overlapCharacters": 200,"includeTimestamps": true},"includeMetadata": true,"maxConcurrency": 5,"maxRetriesPerVideo": 3,"requestTimeoutSecs": 30,"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]},"debug": false}
| Field | Default | Limits and behavior |
|---|---|---|
videoUrls | Required | 1 to 1,000 individual URLs or raw 11-character IDs. |
preferredLanguages | ["en"] | Up to 20 ordered BCP 47 codes; exact matches precede base-language matches. |
captionPreference | manual-first | manual-first, auto-first, manual-only, or auto-only. |
fallbackToAnyLanguage | true | Allows a deterministic fallback when preferences do not match. |
translateTo | Unset | Uses a native target track first, then an advertised YouTube translation. |
outputFormats | ["text", "segments"] | Any unique non-empty selection of text, segments, srt, vtt, markdown, and chunks. |
cleaning | Shown above | Controls entity, Unicode, whitespace, non-speech, deduplication, and paragraph behavior. |
chunking | Shown above | 250 to 20,000 code points per chunk; overlap must be smaller than the maximum. |
includeMetadata | true | When false, every metadata field is null. |
maxConcurrency | 5 | 1 to 20 videos. |
maxRetriesPerVideo | 3 | 0 to 5 complete session retries. |
requestTimeoutSecs | 30 | 10 to 120 seconds per upstream request. |
proxyConfiguration | Residential Apify Proxy | Standard Apify proxy configuration; residential IPs are the reliable default for YouTube. |
debug | false | Emits sanitized phase diagnostics only. |
Supported URLs include youtube.com/watch, youtu.be, /shorts/, /embed/, /live/, music.youtube.com/watch, and youtube-nocookie.com/embed. Watch URLs may contain playlist or tracking parameters; all parameters except the validated video ID are discarded. Credentials, custom ports, foreign hosts, unsupported protocols, and malformed IDs are rejected before any request. Known channel, playlist, profile, and search URLs produce a per-item UNSUPPORTED_INPUT_TYPE row.
Valid inputs are deduplicated by video ID. The first input and inputIndex are retained. Invalid entries are not deduplicated. Dataset order is completion order; use inputIndex to recover source order.
Selection
The Actor normalizes the player caption tracks once. It treats kind=asr as automatic and every other track as manual. Preferred languages are ranked by ordered exact match, then ordered base-language match. Caption preference, default-track status, and original track order break ties deterministically.
When translateTo is set, an existing exact or base-matching native target track wins. Otherwise, the source track is selected normally and translation proceeds only when YouTube advertises the requested target language.
Output
One default dataset item is written per unique video and per invalid input. Success and error rows share schema version 1.0. Every row includes:
- Input identity, status, validated ID, and canonical URL.
- Nullable title, channel ID/name, duration, publication date, thumbnail,
isShort, andisLivemetadata. - Selected/output/source language, caption type, generated/translated flags, and available tracks.
- Requested artifacts plus word, character, and segment counts.
- Provider, extraction duration, external storage references, typed error fields, and fetch time.
Unrequested transcript artifact fields are null. isShort is true only when the retained input is an explicit /shorts/{id} URL; otherwise it is null. Metadata is best-effort and nullable.
{"schemaVersion": "1.0","inputIndex": 0,"input": "aircAruvnKk","status": "success","videoId": "aircAruvnKk","canonicalUrl": "https://www.youtube.com/watch?v=aircAruvnKk","title": "Example video","channelId": "UC000000000000000000000","channelName": "Example Channel","durationSeconds": 212,"publishedAt": "2025-04-10T00:00:00.000Z","thumbnailUrl": "https://i.ytimg.com/vi/aircAruvnKk/hqdefault.jpg","isShort": null,"isLive": false,"languageCode": "en","languageName": "English","sourceLanguageCode": "en","captionType": "manual","isGenerated": false,"isTranslated": false,"availableTracks": [],"transcriptText": "Example transcript text","segments": [],"srt": null,"vtt": null,"markdown": null,"chunks": null,"wordCount": 3,"characterCount": 23,"segmentCount": 1,"provider": "youtubei-transcript-endpoint","extractionMs": 740,"contentStoredExternally": false,"keyValueStoreId": null,"artifactKeys": null,"errorCode": null,"errorMessage": null,"retryable": null,"fetchedAt": "2026-07-31T22:10:00.000Z"}
Text is generated only from normalized source cues. The Actor decodes entities, removes supported caption tags, normalizes whitespace and Unicode when enabled, and never invents punctuation. Adjacent cues are deduplicated only when their text is identical and their shorter time interval overlaps by at least 80%. Chunk sizes count Unicode code points; overlap retains whole segments. Only a single oversized segment is split at word boundaries.
Large Results
Every dataset row is measured as UTF-8 JSON before it is written. If a successful item exceeds 8 MiB, every non-null transcript artifact is stored under stable keys in the default key-value store. Inline artifact fields become null, contentStoredExternally becomes true, and keyValueStoreId plus artifactKeys identify the complete files. Content is never silently truncated.
Errors And Retries
The stable error codes are:
INVALID_VIDEO_INPUT, UNSUPPORTED_INPUT_TYPE, VIDEO_UNAVAILABLE, PRIVATE_VIDEO, MEMBERS_ONLY, AGE_RESTRICTED, LIVE_NOT_ENDED, TRANSCRIPTS_DISABLED, NO_CAPTIONS, NO_MATCHING_LANGUAGE, TRANSLATION_UNAVAILABLE, PO_TOKEN_REQUIRED, RATE_LIMITED, IP_BLOCKED, REQUEST_TIMEOUT, NETWORK_ERROR, YOUTUBE_RESPONSE_CHANGED, BUDGET_EXHAUSTED, and INTERNAL_ERROR.
Only temporary network, proxy, rate-limit, timeout, HTTP 5xx, and unexpected empty-response failures retry. Backoff uses capped exponential jitter and honors Retry-After. One attempt keeps its proxy identity, cookie jar, and anonymous YouTube session. The complete session rotates only for the next attempt.
YouTube increasingly marks some timedtext tracks with exp=xpe, requiring a session-bound Proof of Origin token. HTTP-only anonymous extraction cannot solve this reliably. These tracks, and repeated empty HTTP 200 timedtext responses, return PO_TOKEN_REQUIRED rather than a generic internal error. See the observed PO-token behavior.
Proxies And Privacy
Residential Apify Proxy is enabled by default because YouTube commonly rejects datacenter IPs. You can change the group or provide your own proxy URLs through the standard Console editor. A distinct session ID is generated per video attempt. Redirects are allowed only to the YouTube hosts required by youtubei.js, and timedtext URLs must remain HTTPS www.youtube.com/api/timedtext.
The Actor never logs proxy URLs, cookies, tokens, transcript contents, or raw YouTube response bodies. Debug logs contain only sanitized video IDs, phases, error codes, and retry numbers.
Pay Per Event
Successful rows are written with the custom transcript-extracted event. Error rows are written without a custom event. In the Apify Console pricing configuration:
- Keep the synthetic
apify-actor-startevent enabled. - Define the custom
transcript-extractedevent and its price. - Disable the synthetic
apify-default-dataset-itemevent so error rows are not charged.
The Actor checks remaining event capacity before a success commit and serializes commits across concurrent workers. A successfully charged final item is preserved when the limit becomes exhausted. Remaining work receives free BUDGET_EXHAUSTED rows. See Apify's pay-per-event documentation.
Resume Behavior
STATE in the default key-value store contains configuration-sensitive result hashes and counters. State is updated only after a dataset write and explicitly persisted after every committed item. Restarted runs skip completed keys. Changing a transcript-affecting option creates a different result key. OUTPUT contains the aggregate summary, timing, provider/error counts, charge count, externalization count, and budget state.
Development
Node.js 22 is required. Dependencies are pinned exactly and package-lock.json is committed.
npm cinpm run typechecknpm run lintnpm testnpm run build
Default tests use committed sanitized fixtures and make no YouTube requests. Run the optional live smoke test explicitly:
$RUN_LIVE_TESTS=true npm test -- tests/live
Local Actor execution uses storage/key_value_stores/default/INPUT.json:
$apify run --purge
Limitations And Legal Use
Only public individual videos are supported. Private, members-only, authenticated, and active live content is intentionally unsupported. Captions can be absent, disabled, geo-restricted, rate-limited, IP-blocked, changed by YouTube, or protected by a proof token. No bypass of access controls is attempted.
Use this Actor only where you have the right to retrieve and process the material. Follow YouTube's terms, applicable copyright and privacy law, and the rules of the jurisdiction in which you operate. You are responsible for your inputs, retention, redistribution, and downstream use.