YouTube Transcript Bulk Scraper - Channel and Playlist
Pricing
from $0.40 / 1,000 transcript extracteds
YouTube Transcript Bulk Scraper - Channel and Playlist
Get YouTube transcripts in bulk from video links, a channel or a playlist. Each row has full text, timed cues, language and an auto-generated flag. You also get title, channel, duration and view count. You pay for transcripts only, not for the videos it finds. No API key.
Pricing
from $0.40 / 1,000 transcript extracteds
Rating
0.0
(0)
Developer
Dami's Studio
Maintained by CommunityActor stats
0
Bookmarked
6
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Share
YouTube Transcript Bulk Scraper: full text and timed cues from a list, a channel or a playlist
Paste video links, a channel or a playlist and get one row per video with the whole transcript as plain text, the per-cue timings, the language, and the video's own details next to it. Channels and playlists are expanded into their videos for you, and that expansion costs nothing.
It reads the caption tracks YouTube already holds. There is no speech recognition here, so a video that was uploaded without captions has nothing to return and comes back as an uncharged diagnostic row instead. You pay for transcripts, not for the videos a channel turned out to have.
| Input | Video links, bare 11-character ids, a channel URL or @handle, or a playlist link |
| Output | One row per transcript |
| Ceiling | 500 entries in, up to 5,000 transcripts out |
| Account needed | None from you |
| Price | $0.40 per 1,000 transcripts, flat on every plan |
🔍 What YouTube Transcript Bulk Scraper does
Mix whatever you have in one list. Watch links, Shorts links, youtu.be links, embed links, bare
video ids, a channel URL, an @handle, a playlist link. Channels are read newest first, playlists
in their own order, and a video that shows up twice across two sources is fetched once.
You get the transcript two ways on the same row. text is the whole thing as one readable string,
already joined up, which is what you want if it is going into a model or a search index. segments
keeps every cue with its start, duration and end, which is what you want for jumping to a
moment in the video or building captions.
Language is a preference list, not a filter. Put en, es, pt-BR in and the first one the video
actually has is used; pt matches pt-BR. A human-written track wins over the machine one unless
you say otherwise, because it has punctuation and speaker turns. Whichever track was used is named
on the row, and availableLanguages lists everything the video offered.
📥 What you give it
{"videoUrls": ["https://www.youtube.com/watch?v=aircAruvnKk","https://www.youtube.com/@3blue1brown"],"maxItems": 10,"languages": ["en"]}
| Field | Default | What it is |
|---|---|---|
videoUrls | none | Up to 500 entries, in any of the forms above, mixed freely. |
maxItems | none | Hard ceiling on transcripts returned by the whole run. This is what you pay for, so keep it small while testing. The form starts you at 10. |
maxVideosPerSource | none | How deep to go into each channel or playlist before moving on. Ignored for links that already point at one video. The form starts you at 25. |
languages | none | Language codes in priority order. Leave it empty to take whatever track the video ships with. |
preferAutoGenerated | false | Turn it on when you specifically want the machine transcript even though a written one exists. |
includeSegments | true | Keeps the per-cue array on each row. Turn it off for plain text and much smaller rows. |
concurrency | none | How many videos are read at once, 1 to 10. The form starts you at 6. |
proxyUrls | none | Optional. Your own servers, as http://user:pass@host:port. Leave it empty otherwise. |
Run it with the input empty and you get one labelled sample row, uncharged, so you can look at the shape before you queue a real list.
📤 What you get back
A real row from a real run. text and segments are cut short here; on the real row text runs to
822 characters and segments has all 13 cues:
{"ok": true,"charged": true,"recordType": "transcript","videoId": "6XPlmCDNLNc","url": "https://www.youtube.com/watch?v=6XPlmCDNLNc","title": "The 64 sugar cubes puzzle","channelName": "3Blue1Brown","channelId": "UCYO_jab_esuFRV4b17AJtAw","channelUrl": "https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw","durationSeconds": 55,"viewCount": 322213,"thumbnailUrl": "https://i.ytimg.com/vi/6XPlmCDNLNc/sddefault.jpg?sqp=...","language": "en","languageName": "English","isAutoGenerated": false,"segmentCount": 13,"wordCount": 160,"characterCount": 822,"text": "It's time for a new puzzle of the month. This one comes from a problem on the 2024 British Math Olympiad. ...","segments": [{ "start": 0, "duration": 1.7, "end": 1.7, "text": "It's time for a new puzzle of the month." },{ "start": 2.08, "duration": 3.56, "end": 5.64, "text": "This one comes from a problem on the 2024 British Math Olympiad." }],"availableLanguages": [{ "languageCode": "en", "languageName": "English", "isAutoGenerated": false },{ "languageCode": "en", "languageName": "English (auto-generated)", "isAutoGenerated": true }],"sourceType": "channel","sourceUrl": "https://www.youtube.com/channel/UCYO_jab_esuFRV4b17AJtAw","playlistId": "UUYO_jab_esuFRV4b17AJtAw","scrapedAt": "2026-09-21T01:27:33.286Z"}
| Field | What it is |
|---|---|
text | The transcript joined into one string. wordCount and characterCount measure this field. |
segments | Every cue with start, duration and end in seconds. Present only while includeSegments is on. |
isAutoGenerated | Whether the track actually used was the machine one. Worth keeping, since punctuation and accuracy differ a lot. |
availableLanguages | Every track the video offered, so you can see what else you could have asked for. |
sourceType, sourceUrl | Which of your entries produced this row: video, channel or playlist. Useful when you pasted a mixed list. |
viewCount, durationSeconds | Read off the video itself at the time of the run. |
🧾 Reading the output
Three kinds of row land in your dataset, and recordType separates them.
| Row | How to spot it | Billed |
|---|---|---|
| A real transcript | recordType: "transcript", ok: true | yes |
| The sample row | recordType: "sample", _sample: true | no |
| A diagnostic | recordType: "diagnostic", ok: false, an errorCode | no |
Filter on recordType == "transcript" and you have your transcripts. The charged flag is written
onto the row as it is built, so treat it as the marker that tells a real row from a sample or a
diagnostic, not as a receipt for what was billed.
| Code | What it means |
|---|---|
BAD_INPUT | That entry was not a video, channel or playlist link. Only the first 25 rejects get a row. |
NOT_FOUND | The handle, channel or video does not exist any more. |
NO_RESULTS | Nothing to return: a video with no captions, a channel with no uploads, a private playlist. |
BLOCKED | YouTube refused that video on this run. |
RATE_LIMITED | YouTube asked us to slow down on that video. |
SERVER_ERROR | YouTube answered with an error of its own. |
NETWORK | The connection failed, or the video would not open for a reason it did not name. |
TIME_BUDGET | The run ran out of time before reaching that video. One row per video left over. |
PROXY_INPUT_ADJUSTED | A setting you gave in proxyUrls was not available here and was replaced. |
CHARGE_ERROR | Billing failed on a row that was already delivered. Tell us if you see it. |
UNEXPECTED_ERROR | Something we did not classify. Send the run id. |
▶️ How to run it
- Open YouTube Transcript Bulk Scraper and click Try for free.
- Paste links into Videos, channels or playlists, one per line.
- Set Maximum transcripts low for the first run, since that is what you pay for.
- Put your language codes into Preferred languages in the order you want them tried.
- Click Start, then download the dataset as JSON, CSV or Excel, or pull it from the Apify API.
💰 How much does it cost?
$0.40 per 1,000 transcripts. Flat on every Apify plan, no volume tiers.
Only a delivered transcript counts. Expanding a channel or a playlist into its videos adds nothing, however many it finds. A video with no captions, a rejected entry, a video that would not open and the sample row are all uncharged rows.
A video that appears in two of your sources is fetched once and counted once.
💡 What people use it for
- Feeding a model. Pull a whole channel, keep
text, and you have a corpus that needs no audio handling at all. - Making a video searchable. Keep
segmentsand a hit gives you the second to jump to. - Repurposing. Long-form episodes become newsletter drafts, show notes and clip candidates faster from text than from video.
- Research across creators. Run ten channels and count who talks about what, with dates and view counts already on the row.
🚧 What it does not do
- Captions only. There is no speech recognition. If the uploader published no caption track, no amount of retrying will produce text.
- No translation. You get the tracks the video already carries. Asking for
fron a video with only English gets you the English track, not a translation. - Channels come from the uploads list, newest first. That misses unlisted videos and sometimes
misses Shorts, and there is no date filter, so reach older videos with
maxVideosPerSourcerather than a date range. - A watch link carrying
list=is treated as one video, not as the playlist. Paste the playlist link itself if you want the playlist. - The 5,000 ceiling is the schema's, not a measured promise. One run was measured carrying about 830 transcripts before memory became the limit. Long videos make big rows: a 3 hour 13 minute podcast came back as a single 160 KB row.
- Only the first 25 rejected entries get a
BAD_INPUTrow. Beyond that they are dropped quietly, so check your list if the count looks short. - Private and age-restricted videos are reported as a failure to open rather than as a distinct reason of their own.
🧭 Which YouTube actor do you need?
| If you want | Use |
|---|---|
| Transcripts in bulk from links, channels or playlists | This one |
| The list of videos on a channel, or a keyword search | YouTube Scraper |
| Shorts from a channel or a keyword | YouTube Shorts Scraper |
| Only the new uploads from a watch list | YouTube Upload Monitor |
| The video, audio or subtitle file itself | YouTube Downloader Pro |
❓ Questions people ask
Do I need a YouTube API key? No. No key, no OAuth, no quota to manage.
What if the video has no captions? You get a NO_RESULTS row naming that video and the run
moves on. That row is not charged.
Auto-generated or written captions, which do I get? The written one when the video has it, because it is punctuated. Tick Prefer auto-generated captions to flip that.
Can I get the timings? Yes, that is segments, and it is on by default. Turn it off when you
want smaller rows and only the plain text.
Is scraping YouTube legal? This reads publicly available caption tracks. Transcripts are somebody's work, so what you may do with them afterwards is a copyright question rather than a scraping one. Apify's write-up on the legality of web scraping is a reasonable starting point, and we are not lawyers.
Can I run it on a schedule? Yes. Point it at a channel with a small maxVideosPerSource and
each run picks up what is new at the top.
🆘 If something breaks
Open the Issues tab on the actor page. Include the links you used and the run id. The diagnostic rows in your dataset usually name the reason already.