YouTube Channel & Playlist Transcripts: Bulk & Search
Pricing
from $3.00 / 1,000 transcripts
YouTube Channel & Playlist Transcripts: Bulk & Search
Bulk YouTube transcripts from channels, playlists and keyword searches, with date, length and Shorts filters, a new-only mode and markdown for RAG.
Pricing
from $3.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
Altronite
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
10 hours ago
Last modified
Categories
Share
Get the transcripts of a whole YouTube channel, a playlist or a keyword search in one run. Paste channel links or @handles, playlist links, search keywords or single video links; the actor lists the videos, applies your filters (date, length, Shorts), reads each video's own captions and gives you one row per video, plus one markdown file per source that is ready for an LLM or a RAG index.
You pay only for transcripts you receive. Videos without captions, private or removed videos, videos YouTube keeps blocking, filtered videos and videos already delivered in an earlier run are free.
Who it's for
- LLM and RAG builders who want a channel's or a topic's spoken content as clean text, in one corpus file per source.
- Researchers and analysts who track what is said across a channel, a playlist or a search over time.
- Monitoring and automation (schedules, n8n, Make, Zapier): with
onlyNewSinceLastRun, a daily run returns only the new uploads. - Content teams who repurpose talks, podcasts and tutorials into notes, articles or study material.
What you get
One row per video in the dataset:
| Field | Example |
|---|---|
status | ok or error |
videoId, url | jNQXAC9IVRw, https://www.youtube.com/watch?v=jNQXAC9IVRw |
title, channel, channelUrl, thumbnailUrl | on every ok row |
channelId | from channel, playlist and search listings |
publishedAt, publishedText, publishedAtIsEstimate | 2005-04-24T03:31:52+00:00; exact for a channel's newest uploads, else estimated from text like 3 weeks ago. Channel, playlist and search listings only |
durationSec, viewCount, isShort | from channel, playlist and search listings |
source, sourceType, position | which channel, playlist, search or videos input listed the video, and its rank there |
language, languageCode, isAutoGenerated | English, en, false |
isTranslated, languageFallback, availableLanguages | how the caption track was chosen |
text | the whole transcript as one plain string |
segments / srt / vtt | the timed form, if you ask for it |
segmentCount, wordCount, captionsEndSec | size of the transcript |
scrapedAt | when the row was made, UTC (2026-10-23T22:35:42+00:00) |
reason, error | on error rows only (see Troubleshooting) |
Rows that come from the videos input (links you paste) have no listing behind them, so they carry title, channel, channelUrl and thumbnailUrl but not channelId, the published* fields, durationSec, viewCount or isShort. With no date or length known, the publishedAfter, duration and Shorts filters let those videos through.
A real row (outputFormat: "text", text shortened):
{"status": "ok","videoId": "jNQXAC9IVRw","url": "https://www.youtube.com/watch?v=jNQXAC9IVRw","title": "Me at the zoo","channel": "jawed","channelId": "UC4QobU6STFB0P71PMvOGN5A","channelUrl": "https://www.youtube.com/channel/UC4QobU6STFB0P71PMvOGN5A","publishedAt": "2005-04-24T03:31:52+00:00","durationSec": 19,"source": "https://www.youtube.com/@jawed","sourceType": "channel","position": 1,"publishedText": "21 years ago","publishedAtIsEstimate": false,"isShort": false,"language": "English","languageCode": "en","isAutoGenerated": false,"availableLanguages": ["de", "en"],"segmentCount": 6,"wordCount": 39,"text": "All right, so here we are, in front of the elephants ..."}
In the run's key-value store:
- One markdown file per source, for example
channel-jawed.md,playlist-plzhqobowtqdpd3mizzm2xvfitgf8he-ab.md,search-python-asyncio-tutorial.mdandvideos.md. Each has a header, then every delivered transcript under its title with URL, channel, date, length and language. Turn it off withmarkdownFiles: false. SUMMARY: per source, how many videos were listed, delivered and skipped (and why), and the markdown file's key.
Input
{"channels": ["https://www.youtube.com/@3blue1brown", "@Fireship"],"playlists": ["https://www.youtube.com/playlist?list=PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"],"searchQueries": ["python asyncio tutorial"],"videos": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],"maxVideosPerSource": 20,"publishedAfter": "30 days","skipShorts": true,"outputFormat": "text"}
| Field | Default | What it does |
|---|---|---|
channels | none | Channel URLs, @handles or channel ids (UC...). Uploads are read newest first. |
playlists | none | Playlist URLs or ids. Mixes (list=RD...) and private lists (Watch later, Liked) can't be read. |
searchQueries | none | Keywords searched on YouTube, videos only. Each query is one source. |
videos | none | Single video links or ids. Filters don't apply to these. |
maxVideosPerSource | 50 | Videos taken from each channel, playlist or search after filters, 1 to 5000. |
searchSortBy | relevance | relevance or date (YouTube's upload date order). |
publishedAfter | none | A date like 2026-01-01 or a period like 30 days. |
minDurationSec, maxDurationSec | none | Keep only videos within this length. |
skipShorts | true | Leave out YouTube Shorts. |
onlyNewSinceLastRun | false | Skip videos already delivered for the same source in an earlier run. |
languages | ["en"] | Caption languages in order of preference, like en, de, pt-BR. |
allowAutoGenerated | true | Use YouTube's automatic captions when no human-made track exists. |
translateTo | none | A language code; uses YouTube's own caption translation when allowed. |
outputFormat | text | text, segments, srt, vtt, or metadata (video details only, no transcripts). |
markdownFiles | true | One markdown file per source in the key-value store. |
maxConcurrency | 5 | Videos fetched at once, 1 to 10. |
proxyConfiguration | Apify Proxy on | YouTube limits cloud servers; blocked videos are retried on a fresh IP. |
Run it with an empty input and it fetches the one video of a small public channel, so you can see the output first.
More examples:
The newest 10 long videos of a channel, as a weekly schedule that returns only new uploads:
{ "channels": ["@Fireship"], "maxVideosPerSource": 10, "minDurationSec": 120, "onlyNewSinceLastRun": true }
Keyword search straight to transcripts, last 90 days, newest first:
{ "searchQueries": ["kubernetes tutorial"], "searchSortBy": "date", "publishedAfter": "90 days", "maxVideosPerSource": 25 }
A whole playlist as SRT subtitles, Spanish first:
{ "playlists": ["PLZHQObOWTQDPD3MizzM2xVFitgF8hE_ab"], "languages": ["es", "en"], "outputFormat": "srt" }
Just the video list of a channel (title, date, length, views), no transcripts:
{ "channels": ["@3blue1brown"], "outputFormat": "metadata", "maxVideosPerSource": 500 }
Use it from code or an AI agent
Run it with the Apify API, the Apify Python or JavaScript clients, or as a tool through the Apify MCP server, with the same input JSON. Rows are in the default dataset; the markdown files and SUMMARY are in the default key-value store (GET /v2/key-value-stores/{storeId}/records/{key}). Check each row's status: ok rows have the transcript, error rows say why not.
Pricing
Pay per event:
| Event | Price | When |
|---|---|---|
transcript | $0.003 per transcript | each video whose non-empty transcript is delivered |
video-listed | $0.0003 per video | each video row in the metadata mode (video details only, no transcript) |
apify-actor-start | $0.0005 per run | once per run, one event per GB of memory with a 1 GB minimum (the default is 256 MB) |
Examples:
- A channel's newest 50 videos, 45 with captions: 45 x $0.003 + $0.0005 = $0.1355.
- 1000 videos across 20 channels, 900 with captions: 900 x $0.003 + $0.0005 = $2.7005.
- The video list of a channel, 500 videos, no transcripts: 500 x $0.0003 + $0.0005 = $0.1505.
Free: videos without captions, errors, blocked videos, filtered videos (date, length, Shorts, live), videos already delivered when onlyNewSinceLastRun is on, and sources that could not be listed. A video found by two sources is charged once. In a transcript run, listing is free: only delivered transcripts are charged.
How it works, and what it does not do
- It lists videos the way youtube.com does for a visitor who is not signed in: a channel's public upload lists, a playlist page, or the search results page. No sign-in, no cookies, no browser.
- With
skipShortson, channels are read from YouTube's own long-form and past-live lists, so Shorts are left out exactly. In playlists, where YouTube does not mark Shorts, a video of 60 seconds or less counts as a Short. Live-now and upcoming videos are always skipped. - Dates: listing pages show
3 weeks ago, not a date. The actor turns that into the latest moment it can mean (publishedAtIsEstimate: true), so thepublishedAftercut keeps a borderline video rather than dropping it. A channel's 15 newest uploads get their exact time from the channel's public RSS feed. - Transcripts come from the caption tracks YouTube itself publishes (the same engine as our YouTube Transcript Scraper). No captions, no transcript: it does not run speech-to-text.
- YouTube sometimes refuses cloud servers. Each video is retried on a fresh IP, then blocked videos get a second pass through residential IPs. A video still blocked after that returns
blocked, free. onlyNewSinceLastRunkeeps the delivered video ids per source in your account's named key-value storeyoutube-bulk-transcripts-state. Only delivered videos are remembered, so a new upload whose automatic captions are not ready yet is tried again next run. Delete that store to start over.- Every video gets a row before the run's timeout; videos left when time runs out return
time_limit, free. - Only public data is collected. Transcripts can be copyrighted by the video owner; check your use is allowed.
Troubleshooting
reason | Meaning | What to do |
|---|---|---|
no_captions | The video has no usable captions. | Turn on allowAutoGenerated if it is off. Not charged. |
not_found | The channel, @handle or playlist does not exist, or the playlist is private. | Check the link. |
no_videos | The source listed nothing at all. | Check the link or query. |
invalid_input | An entry is not a channel, playlist or video reference. | See the message on the row. |
unavailable, unplayable, age_restricted | Removed, private, members-only, region-locked or age-restricted video. | Not available without sign-in. |
blocked | YouTube kept refusing the connection. | Run again later. Not charged. |
youtube_error | A transient YouTube error. | Run again. |
time_limit | The run hit its timeout first. | Raise the timeout in Run options or lower maxVideosPerSource. |
empty | The caption track is empty. | Nothing to deliver; not charged. |
not_translatable, translation_unavailable | YouTube can't translate that track. | Remove translateTo. |
failed | Anything else. | Open an issue with the run id. |
| A source shows fewer videos than expected | Filters skipped them | See SUMMARY: skipped counts short, live, too_old, too_short, too_long. |
FAQ
How do I get all transcripts of a YouTube channel?
Put the channel link or @handle in channels and set maxVideosPerSource to the number of videos you want (up to 5000). Uploads are read newest first.
Can I get transcripts for a YouTube search?
Yes. Put keywords in searchQueries; each query's results are listed and transcribed like a playlist.
How do I get only new videos every day?
Turn on onlyNewSinceLastRun and schedule the actor. Each run skips what earlier runs delivered for the same source, and you pay only for the new transcripts.
Does it include Shorts?
Not by default. Set skipShorts: false to include them.
Why is publishedAt an estimate?
YouTube's listing pages show relative dates. Exact times come from the channel RSS feed for the newest 15 uploads; others are estimated and flagged.
Can it transcribe videos without captions? No. It reads existing caption tracks only, human-made or automatic.
Limits
- Up to 5000 videos per source; a search returns as many results as YouTube serves for it.
- Mixes, Watch later, Liked videos, members-only and private content can't be read.
- Single videos have no duration, date or views (their title and channel come from YouTube's oEmbed data).
Integrations
Call it from the Apify API, Python or JavaScript clients, Make, Zapier, n8n or an MCP client; get the dataset as JSON, CSV, Excel or XML, and the markdown files from the key-value store.
Related tools
- YouTube Transcript Scraper: transcripts for a list of video links.
- Google Sheets Export & Import: send any run's results to a Google Sheet.
Support
Open an issue on the Issues tab with the source and the run id. Error rows and SUMMARY say why a video or source gave nothing, which usually answers it. Published by Altronite.