YouTube Transcript Scraper — Never Silent, LLM-Ready avatar

YouTube Transcript Scraper — Never Silent, LLM-Ready

Pricing

from $8.00 / 1,000 transcript delivereds

Go to Apify Store
YouTube Transcript Scraper — Never Silent, LLM-Ready

YouTube Transcript Scraper — Never Silent, LLM-Ready

Public YouTube transcript scraper with full batch accounting: every input returns a transcript or a typed, actionable failure — the run is never silent. Supports direct videos plus playlist/channel expansion, caption-availability auditing, and LLM-ready output as JSON, text, SRT, WebVTT, or chunks.

Pricing

from $8.00 / 1,000 transcript delivereds

Rating

0.0

(0)

Developer

Catalin Ionut Iliescu

Catalin Ionut Iliescu

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

8 days ago

Last modified

Categories

Share

Extract public YouTube captions at scale with honest batch accounting. In a normally completed run with functioning platform storage, every accepted input returns either a transcript or a typed, actionable failure. Feed it public video URLs, playlists, or channels and get back clean text, timestamped segments, SRT/WebVTT subtitles, or LLM-ready chunks.

Python-only runtime. No Node, YouTube account, retained cookies, API key, audio download, or browser automation.

Why this Actor

Most transcript scrapers quietly drop the videos they can't handle: you send 100 URLs, get 63 rows back, and have no idea what happened to the other 37. This one is built around a different promise — complete accounting:

  • Never silent. In a normally completed run with functioning platform storage, every input produces one accounted result. Successes carry the transcript; failures carry a typed status (no_captions, video_unavailable, rate_limited, blocked, language_unavailable, …) plus a plain-English remediation.
  • Typed, actionable failures. Each failure says what went wrong and whether retrying will help, so you can react programmatically instead of guessing.
  • Playlist & channel expansion. Give it a public playlist or channel and it lists the videos (a bounded maxItems prefix, newest-first for channels) and extracts each transcript, with a parent-level sourceExpansionRecord accounting for what was discovered, admitted, and omitted.
  • Caption‑availability audit mode. A text‑free mode that reports which caption tracks and languages a video advertises — manual vs. automatic — without downloading any transcript.
  • LLM‑ready output. Deterministic overlapping chunks with token estimates, ready to embed or feed to a model.
  • Run‑level accountability report. A machine‑readable summary in the key‑value store reconciles every expected input against what was actually written.

⚠️ Proxy: use Residential

YouTube blocks datacenter IP ranges, and the Apify cloud runs on datacenter IPs. Because of this, proxyMode defaults to residential, which is the only mode that reliably reaches YouTube from the cloud. direct, apify_auto, and datacenter are frequently rate-limited or blocked — and when that happens the Actor tells you so with a typed blocked / rate_limited result rather than silently returning nothing.

The residential proxy cost is included in the per-transcript price. You are not billed separately for proxy traffic, and you do not need to supply your own proxy. Measured on real runs, residential transfer is 85–96% of what this Actor actually costs to operate — that cost is carried in the price rather than passed through to you.

What you are charged for

Charging is driven by what actually lands in your dataset, so you pay for delivered output and never for work the Actor didn't do. See the pricing box on the Store page for exact figures.

Two rules decide every video row:

  1. No attempt, no charge. If the Actor never made a network request for an input, that row is free.
  2. You pay for answers about the video, never for our routing problems. A transcript, a caption inventory, or a definitive verdict about the video itself is chargeable. A blocked, rate-limited, or timed-out request is not.
OutcomeCharged
Delivered transcriptFull transcript price
Caption-audit answer (available / none)Audit price, below a transcript
Definitive verdict about the video — no_captions, language_unavailable, video_unavailable, age_restricted, region_blockedHalf the transcript price
Playlist/channel listing that returned videosListing price, never a transcript price
Duplicate video already extracted in this runAn eighth of the transcript price
blocked, rate_limited, timeout, upstream/proxy errorsFree — our problem, not yours
Inputs beyond maxVideos, invalid URLsFree — no network work was performed
Inputs skipped after a charge limit or a dead routeFree — never attempted
Listing that returned no videosFree
Rows the dataset never confirmedFree

When a run stops early

Two conditions stop the Actor from spending on work it cannot deliver or bill:

  • Charge limit reached. If you set a maximum charge for the run, extraction stops rather than producing output past your limit. Remaining inputs return extraction_error / CHARGE_LIMIT_REACHED.
  • Route definitively blocked. After three consecutive blocked/rate_limited results with no success in between, the Actor stops rather than repeating a failing request for every remaining video. Remaining inputs return blocked / REQUEST_BLOCKED with attempts: [].

In both cases every remaining input still gets its own typed, unbilled row explaining why it was not attempted, and the ACCOUNTABILITY_REPORT carries a runStop section (stopped, stopReason, skippedInputCount). The OUTPUT summary surfaces runStopped and runStopReason.

Input

Transcript mode (default)

{
"videoUrls": [
"https://www.youtube.com/watch?v=VIDEO_ID",
"https://youtu.be/VIDEO_ID"
],
"sources": [
{ "type": "playlist", "value": "PUBLIC_PLAYLIST_ID", "maxItems": 25 },
{ "type": "channel", "value": "@public_handle", "maxItems": 25 }
],
"languages": ["en"],
"languageFallback": true,
"outputFormat": "llm",
"includeMetadata": true,
"includeSegments": true,
"includeTranscriptText": true,
"includeChunks": true,
"maxVideos": 25,
"maxConcurrency": 3,
"perVideoTimeoutSecs": 30,
"proxyMode": "residential"
}

videoUrls accepts watch, youtu.be, Shorts, embed, and live URL shapes. sources is an optional ordered list of video, playlist, or channel entries; videoUrls and sources can be combined.

Expansion behavior. Playlist and channel expansion reads YouTube's current public listing format and returns each source's videos as a bounded maxItems prefix (channels newest‑first), with a parent sourceExpansionRecord accounting for discovery. Sources larger than maxItems are reported limited. Because it relies on an unofficial listing surface, a future YouTube format change fails safe with a typed source_protocol_error rather than dropping videos silently; direct videoUrls are always unaffected.

Caption‑audit mode

{
"operationMode": "caption_audit",
"videoUrls": ["https://youtu.be/VIDEO_ID"],
"languages": ["en", "de"],
"includeMetadata": true,
"proxyMode": "residential"
}

Audit mode never downloads a transcript. Each row reports the advertised caption inventory — track count, available languages, and whether captions are manual, automatic, or of unknown generation — or a fail‑closed unknown when a complete inventory can’t be safely established. languages expresses optional interests and is reported as available/unavailable without filtering the full inventory.

Key options

OptionDefaultNotes
outputFormatllmjson, text, srt, vtt, or llm (text + chunks).
languages["en"]Priority‑ordered caption language codes.
languageFallbacktrueUse another advertised track if the preferred language is missing.
includeSegments / includeTranscriptTexttrue / trueAt least one must stay enabled; current successful rows always retain both core payloads.
includeChunks / chunkMaxTokens / chunkOverlapTokenstrue / 1000 / 100LLM chunking; overlap ≤ half of max.
maxVideos25Fetch budget. Inputs beyond it still get a typed MAX_VIDEOS_LIMIT row.
maxConcurrency3Videos processed at once.
perVideoTimeoutSecs30One absolute deadline per video (max 120).
proxyModeresidentialSee the proxy note above.
proxyCountry""Optional two‑letter country code.
debugModefalseAdds safe per‑attempt caption/HTTP diagnostics (never secrets).

Output

Each result is pushed to the dataset as soon as it finishes, so dataset order is completion order — sort by inputIndex if you need input order. A successful transcript row looks like:

{
"inputIndex": 0,
"videoId": "M7lc1UVf-VE",
"canonicalUrl": "https://www.youtube.com/watch?v=M7lc1UVf-VE",
"status": "ok",
"statusDetail": "Extracted 466 caption segments in 'en' through youtube-transcript-api.",
"errorCode": "",
"remediation": { "actionCode": "OK_NO_ACTION", "retryClass": "none", "retryRecommended": false },
"languageUsed": "en",
"languageAvailable": ["en"],
"isAutoGenerated": false,
"title": "YouTube Developers Live: Embedded Web Player Customization",
"channelName": "Google for Developers",
"durationSeconds": 1344,
"viewCount": 1642048,
"publishedAt": "2013-04-10T10:25:04-07:00",
"transcriptText": "JEFF POSNICK: Hey, everybody. Welcome to this week's show ...",
"segments": [ { "text": "JEFF POSNICK: Hey, everybody.", "startSeconds": 10.349, "durationSeconds": 1.0, "endSeconds": 11.349 } ],
"chunks": [ /* deterministic overlapping LLM chunks with token estimates */ ],
"transcriptCharCount": 20676,
"tokenCountEstimate": 5169,
"extractionStrategy": "youtube_transcript_api",
"proxyModeUsed": "residential",
"fetchedAt": "2026-07-14T18:06:05Z"
}

A failed row carries empty transcript payloads plus the typed diagnosis:

{
"inputIndex": 2,
"videoId": "…",
"status": "video_unavailable",
"errorCode": "VIDEO_UNAVAILABLE",
"statusDetail": "The library reports that the public video is unavailable.",
"remediation": { "actionCode": "CHECK_PUBLIC_AVAILABILITY", "retryClass": "manual_review", "retryRecommended": false },
"transcriptText": "", "segments": [], "chunks": []
}

Playlist/channel sources emit a sourceExpansionRecord describing what was discovered (expansionStatus, admittedChildren, discoveryCompleteness, ordering, and a typed primaryError + remediation on failure). Expanded child videos are ordinary transcript rows tagged with their sourceType and sourceInputIndex.

Statuses

ok, no_captions, language_unavailable, video_unavailable, age_restricted, region_blocked, blocked, rate_limited, timeout, invalid_url, extraction_error, unknown_error. Read status, errorCode, statusDetail, and remediation together — null metadata alone never means success.

Accountability report

After processing, the Actor writes an ACCOUNTABILITY_REPORT record to the default key‑value store: expected vs. accounted vs. missing inputIndex values, status/error/strategy tallies, and (for expansion runs) source‑ and child‑level counts. accountingComplete is true only when every expected input has a confirmed dataset row.

It also carries a charging section (what the run billed, and what it would have billed) and a runStop section describing any early stop.

Extraction architecture

One proven primary engine, with corroborating fallbacks that share code with the audit feature:

  1. youtube-transcript-api — the primary, battle‑tested engine. It fetches captions through YouTube’s anonymous InnerTube player surface and wins the vast majority of transcripts.
  2. Anonymous InnerTube fallbacks (android_innertube, direct_timedtext, innertube_player) — Actor‑owned public player/timed‑text paths tried only if the primary can’t produce a usable transcript. They also produce the positive caption‑availability evidence that powers caption‑audit mode, so they’re not dead weight.

All paths use anonymous public surfaces only, run through the selected proxy, discard cookies, and never accept or emit keys, tokens, caption URLs, or proxy credentials. The chain stops at the first usable transcript and records every attempt (visible with debugMode).

Limitations

  • Public captions only. Age‑restricted, private, deleted, members‑only, and login‑required videos are intentionally unsupported. Translations are not generated — languageFallback selects an existing alternate track, it does not machine‑translate.
  • YouTube can change. These are anonymous web/player surfaces with no official public transcript API; upstream changes can require maintenance.
  • Playlist/channel expansion is bounded. Each source returns up to a maxItems prefix (1–50, newest‑first for channels); larger sources are reported as limited. It reads YouTube's current public listing surface, so upstream format changes fail safe with a typed error rather than wrong data; direct video URLs are unaffected.
  • Proxy‑dependent. Reliable cloud extraction requires residential egress (see above).
  • Token counts are approximate and deterministic, not tied to a specific tokenizer.
  • Live/premiere/just‑uploaded videos and very recently changed captions are less predictable.

Use from the Apify Console, API, or integrations

Run the Actor from its Input tab, save reusable tasks, call it through the Apify API, or connect it to your automation and AI workflows.

Results are available in the default dataset, while the bounded run summary and full accountability report are available from the run output and key-value store.

Only use this Actor for content you are entitled to process. Public availability does not grant ownership or remove copyright, privacy, or platform‑policy obligations.