YouTube Transcript Scraper — RAG-Ready
Pricing
from $5.00 / 1,000 transcripts
YouTube Transcript Scraper — RAG-Ready
Turn YouTube into RAG-ready transcripts: chapter-grouped, chunk-ready paragraphs for LLM and search pipelines. Re-runs skip already-delivered videos to keep a corpus fresh, and failed videos are never billed. Accepts mixed video, playlist, and channel URLs. Unofficial; public data only.
Pricing
from $5.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
Blackcube Agency AB
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
36 minutes ago
Last modified
Categories
Share
More from this account: Website Contact & Email Suite · Career Site & ATS Jobs Suite · Google News Suite · Keyword Research Suite · Shopify Store Intelligence Suite · eBay Data Suite · Amazon Reviews Suite · Reddit · Meta Ad Library · Vinted
RAG-ready YouTube transcripts — chapter-grouped, chunk-ready paragraphs.
Point it at YouTube videos, playlists, or channels and get back structured JSON that drops straight into a retrieval or LLM pipeline: caption text merged into clean paragraphs, grouped under the video's own chapters, with millisecond timing on every segment.
Unofficial — not affiliated with, endorsed by, or connected to YouTube or Google. This actor works only with data that YouTube already serves publicly. No YouTube logo or trademark is used in its name or branding.
Why this one
1. Chapter-grouped, LLM-ready paragraphs
Raw caption tracks are a wall of 2–5 word cues with no sentence or topic boundaries — useless for chunking. This actor merges those cues into readable paragraphs (breaking on pauses and sentence endings, targeting ~500–1,500 characters) and groups them under the video's chapters, so each chunk already carries its topic and its timestamp.
// one chapter from a real run (abridged){"title": "Introduction","startMs": 0,"endMs": 112480,"paragraphs": [{"startMs": 0,"endMs": 34640,"text": "In this course, I'm going to teach you everything you need to know to get started programming in Python. Now, Python is one of the most popular programming languages out there…"},{"startMs": 81200,"endMs": 112480,"text": "We're going to talk about all the core concepts in Python. We're going to look at everything you need to know to start programming in Python…"}]}
Videos without chapters still come back grouped — as a single group with a null title — so downstream code has one shape to handle. See .actor/sample-output.json for two complete records from a real run.
2. Freshness re-runs skip what you already have
Turn on Only new videos and give the run a state label. Every delivered transcript is remembered under that label, so a scheduled re-run over the same channel or playlist fetches — and bills — only the new uploads. Keep a corpus current without re-paying for videos you already have.
3. Failures are free
Billing is per delivered transcript — one charge for each caption track this actor actually hands you. Every failure mode — captions disabled, region-locked, members-only, a dead playlist entry, a bot-check block — is pushed as a documented error item at no charge. You pay only for transcripts you receive; errors are transparent and cost nothing.
Supported input scope: a single run accepts a mixed list of video, playlist, channel, and @handle URLs — each playlist or channel is expanded into its videos automatically, then de-duplicated across sources.
Input
| Field | Type | Default | What it does |
|---|---|---|---|
| urls (required) | array of strings | — | Mixed list of YouTube URLs: individual videos (watch?v=, youtu.be/, /shorts/, /embed/, /live/), playlists (list=), channels (/channel/UC…, /@handle, /c/…, /user/…), and channel tab URLs. Playlists and channels are expanded to their videos. A watch?v=…&list=… URL is treated as the single video. |
| languages | array of strings | (empty) | Ordered language preference (BCP-47 tags or prefixes, e.g. en, pt-BR). First available match wins. Empty → the video's default caption track. |
| allLanguages | boolean | false | Return every caption track instead of the best match. ⚠️ This emits one dataset item per track, and each track is billed as one transcript. Ignores the languages list. |
| includeShorts | boolean | false | When expanding a channel, also include Shorts. Off = long-form uploads only. |
| maxVideosPerSource | integer | 100 | Cap on videos taken from each playlist or channel URL (1–5000), applied per source. Direct video URLs are unaffected. |
| includeChapters | boolean | true | Extract chapter markers and the upload date. Turning it off saves one request per video but leaves uploadDate null (they share the same source call). Transcripts are still grouped — under one null-title chapter. |
| formats | array | (empty) | Extra rendered formats to add to each item: text (plain text), srt, vtt. Structured JSON segments and paragraphs are always included. |
| onlyNewVideos | boolean | false | Skip videos already delivered by previous runs sharing the same state label. For scheduled re-runs that should pick up only new uploads. |
| stateLabel | string | default | Names the persistent delivered-video memory used by Only new videos. Runs sharing a label share that memory. |
| proxyConfiguration | object | Apify Proxy | Proxy settings for YouTube requests. YouTube blocks most cloud IPs, so a residential proxy is recommended for caption fetching; you may also supply your own proxy URLs. |
Overlapping schedules: Only new videos state is read at run start and written per delivery (best-effort). Two runs sharing a state label that overlap in time may both deliver the same brand-new video. Schedule runs on the same label so they don't overlap.
Output
Every result is one JSON record in the run's dataset. A delivered transcript looks like this:
| Field | Type | Notes |
|---|---|---|
videoId | string | e.g. jNQXAC9IVRw. |
url | string | Canonical watch URL. |
title, channel, channelId | string | Video and channel identity. |
uploadDate | string | null | YouTube's own absolute date string (hl=en, e.g. "Jul 11, 2018"). null when includeChapters is off. |
durationSeconds | number | Video length. |
language | string | Language tag of the delivered track. |
matchedLanguageTag | string | The track's BCP-47 language tag. Under allLanguages, duplicate same-language tracks get a #2/#3 suffix to stay distinct; use language for the clean code. |
isAutoGenerated | boolean | true for ASR (auto) captions, false for a manual track. |
availableLanguages | array | Every caption track on the video: { languageCode, name, autoGenerated }. |
sourceUrls | array | Every input URL that produced this video (a video reached from two playlists lists both). |
sourcePosition | number | null | Zero-based position within its expanded playlist/channel; null for direct video URLs. |
segments | array | Raw caption cues in order: { startMs, durMs, text }. |
chapters | array | The RAG wedge: { title, startMs, endMs, paragraphs: [{ startMs, endMs, text }] }. Always present; one null-title group when the video has no chapters. |
plainText | string | null | Full transcript as text. Present only when formats includes text. |
srt, vtt | string | null | Subtitle renderings. Present only when requested in formats. |
A failed video is a record with errorReason set and the transcript fields empty. It carries errorReason, rawReason (the raw upstream detail, for debugging), the offending input or videoId, and sourceUrls.
Dataset views
The dataset ships two views in the Apify Console:
- Transcripts — the transcript columns.
- Errors —
input,videoId,errorReason,rawReason,sourceUrls.
Views project columns, they do not filter rows. Both delivered transcripts and error items appear in both views (with the other view's columns blank). To separate them in your own code, split on
errorReason: it isnullon a delivered transcript and set on a failure.
See .actor/sample-output.json for two full records from a real run (freeCodeCamp's Learn Python course and Me at the zoo).
Failure modes
Every failure is a free error item — you are never billed for one. The errorReason is a stable machine-readable label; rawReason carries YouTube's own (locale-sensitive) detail.
errorReason | Meaning | Billed? |
|---|---|---|
captionsDisabled | The video has no caption tracks at all (captions turned off by the uploader). | No — free |
noCaptions | Caption tracks exist, but none match the languages you requested. | No — free |
regionRestricted | The video isn't available in the region the request came from. | No — free |
membersOnly | The video is restricted to channel members. | No — free |
ageRestricted | The video is age-gated and needs a signed-in account. | No — free |
videoUnavailable | The video was removed, deleted, made private, or never existed (includes [Private video] / [Deleted video] playlist entries). | No — free |
notYetAvailable | An upcoming premiere or offline live stream — no transcript yet. | No — free |
invalidUrl | The input isn't a recognizable YouTube video, playlist, or channel URL. | No — free |
expansionFailed | A playlist or channel URL couldn't be listed (its videos couldn't be enumerated). | No — free |
emptyTranscript | A track was found but holds only music/noise markers — no spoken text. | No — free |
potRequired | YouTube demanded a proof-of-origin token for this track; it can't be fetched on the current path. | No — free |
blocked | The request was bot-checked or returned empty and kept failing after retries on fresh IPs. | No — free |
unknown | An uncategorized failure — the raw upstream reason is captured in rawReason for debugging. | No — free |
Language support
- BCP-47 prefix matching. A request for
enmatchesen,en-US, anden-GB(but nevereng);pt-BRmatches only Brazilian Portuguese. Yourlanguageslist is tried in order — the first entry with any available match wins. - Manual beats auto. Within a matched language, a human-made caption track is preferred over an auto-generated (ASR) one, and an exact tag beats a prefix match.
- See what's on offer. Every delivered item lists all real caption tracks in
availableLanguages(manual + auto, never machine-translation targets), so you can widen yourlanguageslist or switch onallLanguageswith full knowledge of what exists.
Unofficial project. Uses only publicly available YouTube data. Not affiliated with YouTube or Google.
Run it without configuring anything — Get the transcript of a YouTube video, a ready-made example you can start as-is or copy.
Use cases
- Build a RAG corpus. Turn a channel, playlist or URL list into chunk-ready paragraphs with the source video and timestamp on every row — ready to embed.
- Repurpose long video. Pull the spoken text of a talk or podcast and turn it into a post, a newsletter or a clip list without watching it.
- Search what was said. Make a back catalogue greppable: find every mention of a product, a name or a claim across hundreds of videos.
- Keep a corpus fresh. Schedule it and re-runs skip videos already delivered, so you pay for new material only.
Run it on a schedule
A one-off pull answers a question; a schedule answers it every day without you. Open Schedules in the Apify Console, point a cron at this Actor, and the dataset keeps filling on its own — no server, no cron box, no babysitting. Everything here is built to be re-run: you are billed per transcript delivered, so a scheduled run that finds nothing new costs nothing.
FAQ
Do I need a YouTube API key?
No. No key, no login, no OAuth and no YouTube quota to manage — you give it a URL and it returns text.
Can I export transcripts to CSV, JSON or Excel?
Yes. Every run writes a dataset you can export in one click from the Console, or pull straight from the API in JSON, CSV, XLSX or JSONL.
What happens to a video with no captions?
It comes back as a free row that says so, rather than failing the run. You are never charged for a video that returned no transcript.
Can I transcribe a whole channel or playlist at once?
Yes — pass the channel or playlist URL and it walks the uploads for you. There are dedicated Actors in this suite for both.
Something wrong, or a field you need that is missing? Open an issue on the Issues tab — it is read and it gets fixed. If this saved you time, a rating on the Store page helps the next person find it.