YouTube Transcript & Subtitles API: Bulk, Channels, Playlists
Pricing
from $5.00 / 1,000 transcripts
YouTube Transcript & Subtitles API: Bulk, Channels, Playlists
Get YouTube transcripts and subtitles (SRT/VTT) in bulk, from videos, channels or playlists. $5 per 1,000 on paid plans, and you only pay for successful transcripts.
Pricing
from $5.00 / 1,000 transcripts
Rating
0.0
(0)
Developer
Nawfel
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Get YouTube transcripts and subtitles (SRT/VTT) in bulk: one video, a list, a whole channel, or a playlist, as clean JSON with timestamps.
Compatible with the common videoUrl + targetLanguage input and {"data": [{start, dur, text}]} output, so a single-video pipeline can switch by changing only the Actor ID.
$5 per 1,000 transcripts on paid plans, and you only pay for successful transcripts. Videos without captions are free.
What you get
- Transcripts with timestamps: every caption line with its start time and duration, plus the full text as one string.
- Bulk, channels and playlists: pass hundreds of video URLs, a channel (
@handle), or a playlist in one run. - Subtitles: standard SRT and WebVTT files, ready to upload or burn in.
- AI-ready chunks: transcript split into pieces of the size you choose, each with its time range, chapter, and a link that opens the video at that exact moment (
&t=123s). Built for RAG pipelines and chatbots that cite their sources. - Chapters and video details: title, channel, duration, view count, and the video's chapters ("Intro", "Pricing", ...) with timestamp links.
- Find the moment: give keywords ("pricing", "Bitcoin", a brand name) and get every moment they are said, with links.
- Channel monitoring: schedule the Actor on a channel and each run returns only the new videos' transcripts.
- Markdown and paragraphs: readable text grouped by pauses, with clickable timestamps, for blog posts and notes.
- Reliable: blocked requests are retried automatically on fresh connections, and region-locked videos are retried from other countries. Runs don't fail because one video has no captions.
Pricing
| Price | |
|---|---|
| Successful transcript, paid Apify plans | $5 per 1,000 ($0.005 each) |
| Successful transcript, free Apify plan | $10 per 1,000 |
| Video without captions, private, deleted, age-restricted... | free |
| Start fee, compute, proxies | included |
Chapters, chunks, SRT, VTT, Markdown, paragraphs and keyword search are included at no extra cost. The spending limit you set on a run is respected: the run stops cleanly when it is reached, and you are never charged above it.
Quick start
One video (the classic input):
{"videoUrl": "https://www.youtube.com/watch?v=8KkKuTCFvzI","targetLanguage": "en"}
Many videos: videoUrl and videoUrls accept watch URLs, youtu.be links, Shorts, embed and live URLs, or bare 11-character IDs. Duplicates are removed.
{"videoUrls": ["https://www.youtube.com/watch?v=8KkKuTCFvzI","https://youtu.be/dQw4w9WgXcQ","https://www.youtube.com/shorts/BR06T1eJ3go"],"targetLanguage": "en"}
A channel or a playlist (newest videos first, up to maxVideos):
{"channelUrl": "https://www.youtube.com/@TED","playlistUrl": "https://www.youtube.com/playlist?list=PLxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx","maxVideos": 100}
Subtitles and AI chunks:
{"videoUrls": ["https://www.youtube.com/watch?v=8KkKuTCFvzI"],"chunkSize": 1500,"extraFormats": ["srt", "vtt", "markdown", "paragraphs"]}
Find every moment a word is said across a channel (outputFormat: "none" keeps the results small):
{"channelUrl": "@veritasium","maxVideos": 50,"searchKeywords": ["black hole", "quantum"],"outputFormat": "none"}
Monitor a channel: create a Schedule (daily or weekly) with this input. Each run returns only videos it hasn't delivered before.
{"channelUrl": "@mkbhd","maxVideos": 20,"onlyNewVideos": true}
Input
| Field | Type | Description |
|---|---|---|
videoUrl | string | One video URL or ID. |
targetLanguage | string | Transcript language, ISO 639-1 (en, es, pt, fr, de, ja, ...). Default en. |
videoUrls | array | Many video URLs or IDs. |
channelUrl | string | @handle, channel URL, or UC... channel ID. |
playlistUrl | string | Playlist URL or ID. |
maxVideos | integer | Videos taken from the channel and from the playlist. Default 50. |
outputFormat | string | both (default), segments (data only), text (text only), none. |
fallbackToAnyLanguage | boolean | If the requested language isn't available, return the video's own language (flagged). Default true. |
searchKeywords | array | Words or phrases to find; returns matches. |
chunkSize | integer | Split into chunks of up to this many characters (e.g. 1000-2000). 0 = off. |
extraFormats | array | Any of srt, vtt, markdown, paragraphs. |
onlyNewVideos | boolean | Monitoring: skip videos delivered by earlier runs of the same input. |
monitorStateKey | string | Optional name to keep several monitors on the same channel separate. |
Give at least one of videoUrl, videoUrls, channelUrl, playlistUrl.
Output
One dataset item per video. data is first and has the same shape as the common single-video format; everything else is added alongside it.
{"data": [{ "start": "0.280", "dur": "4.440", "text": "Welcome back to the channel." },{ "start": "4.720", "dur": "3.960", "text": "Today we're looking at pricing." }],"videoId": "8KkKuTCFvzI","videoUrl": "https://www.youtube.com/watch?v=8KkKuTCFvzI","title": "Example video title","channelName": "Example Channel","channelId": "UCxxxxxxxxxxxxxxxxxxxxxx","durationSeconds": 754,"viewCount": 182345,"chapters": [{ "title": "Intro", "start": 0, "end": 95, "startTime": "0:00", "url": "https://www.youtube.com/watch?v=8KkKuTCFvzI&t=0s" },{ "title": "Pricing", "start": 95, "end": 754, "startTime": "1:35", "url": "https://www.youtube.com/watch?v=8KkKuTCFvzI&t=95s" }],"language": "en","requestedLanguage": "en","isAutoGenerated": false,"isLanguageFallback": false,"availableLanguages": ["en", "es"],"text": "Welcome back to the channel. Today we're looking at pricing. ...","status": "success","errorType": null,"errorMessage": null}
With options, items also contain:
chunks:[{ index, start, end, startTime, url, chapter, text }]matchesandmatchCount:[{ keyword, start, startTime, url, chapter, context }]srt,vtt,markdown: strings;paragraphs:[{ start, startTime, url, text }]
For very long videos (several hours) with many formats, the biggest formats are saved to the run's key-value store and the item links to them (srtUrl, vttUrl, ...) to stay within dataset size limits.
The dataset has three views: Transcripts, Details (video details and language handling), and Moments (one row per keyword match).
Errors
Videos without a transcript are still listed, so you can see what happened, and are never charged:
errorType | Meaning |
|---|---|
NO_CAPTIONS | The video has no captions (manual or auto-generated). |
LANGUAGE_NOT_AVAILABLE | No captions in targetLanguage and fallbackToAnyLanguage is off. availableLanguages lists what exists. |
VIDEO_UNAVAILABLE | Private, deleted, or otherwise unavailable. |
AGE_RESTRICTED | Age-restricted videos need a signed-in account, which this Actor never uses. |
MEMBERS_ONLY | Channel-members-only video. |
LIVE_NOT_FINISHED | A live stream or premiere that hasn't ended yet. |
GEO_RESTRICTED | Not available in any country we tried. |
INVALID_URL | Not a YouTube video URL or ID. |
BLOCKED_AFTER_RETRIES | YouTube refused repeatedly. Rare; try again later. |
A run only fails if the input itself is invalid (for example, no video, channel or playlist given).
Migrating from a single-video transcript Actor
If you call a transcript Actor with {"videoUrl": ..., "targetLanguage": ...} and read items[0]["data"], change only the Actor ID:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_API_TOKEN>")run = client.actor("nawfel_bc/youtube-transcript-api").call(run_input={"videoUrl": "https://www.youtube.com/watch?v=8KkKuTCFvzI","targetLanguage": "en",})items = client.dataset(run["defaultDatasetId"]).list_items().itemssegments = items[0]["data"] if items else None # [{"start": "0.280", "dur": "4.440", "text": "..."}]
What stays the same with a classic single-video input (only videoUrl):
- The dataset item has
datawithstart/dur/textas strings, in seconds with 3 decimals. - The key-value store record
outputholds the same object; when there is no transcript it holds{"success": false, "videoUrl", "targetLanguage", "error": {"code", "message", "retryable"}}, with codeNO_CAPTION_AVAILABLEfor videos without captions. - When there is no transcript, the dataset stays empty and the run still succeeds, with the reason in the status message.
One difference: this Actor never machine-translates. If the video has no captions in targetLanguage, you get the video's own language with isLanguageFallback: true (or LANGUAGE_NOT_AVAILABLE if you turn fallback off).
Then, to process many videos, send them all in one run with videoUrls instead of one run per video: it is faster and you avoid per-run overhead.
Use cases
- RAG and AI agents: index transcripts with
chunkSize, and let your assistant cite the exact moment with the chunkurl. - Content repurposing: turn videos into blog posts, newsletters and social posts with
markdownorparagraphs. - Subtitles: export
srtorvttfor editing, accessibility, or re-uploading. - SEO: publish transcripts next to embedded videos.
- Research and monitoring: track what competitors, creators or news channels say with
searchKeywordsand scheduledonlyNewVideosruns.
Use from AI agents (MCP)
This Actor can be used as a tool through the Apify MCP server. An agent can call it with {"videoUrl": "...", "outputFormat": "text"} for a quick summary input, or with chunkSize and searchKeywords to retrieve and cite specific moments.
FAQ
Which videos work? Public YouTube videos that have captions, manual or auto-generated, including Shorts and finished live streams.
Can I get a transcript in another language? Only if the video has captions in that language. Transcripts are never machine-translated. Use availableLanguages to see what exists.
What about videos without captions? They return NO_CAPTIONS and are free. Speech-to-text transcription is not included.
Does a failed video fail the run? No. Each video gets its own item with status and errorType.
How fast is it? Up to 20 videos are processed at once; a typical video takes 1 to 5 seconds.
Where are chapters from? From the timestamps in the video's description ("0:00 Intro"), using YouTube's rules: at least three, starting at 0:00. Videos without them return chapters: [].
How does monitoring remember what it already delivered? In a key-value store named youtube-transcript-monitor in your own Apify account. Videos whose captions aren't ready yet are retried on later runs for up to 7 days, and charged only when they succeed.
Do I need a YouTube account or API key? No.
Limitations
- Only public videos; never private, members-only or age-restricted ones.
- One transcript language per run.
- Chapters only when the description lists them.