YouTube Captions API: Any Language, Timestamps
Pricing
from $1.19 / 1,000 transcripts
YouTube Captions API: Any Language, Timestamps
YouTube captions API: send video, channel, playlist or search inputs and get each video's captions as timed segments and full text, manual or auto-generated, in any language (translated with timings kept), with every caption track listed. JSON, CSV or SRT. No YouTube API key.
Pricing
from $1.19 / 1,000 transcripts
Rating
0.0
(0)
Developer
DeepMine
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
YouTube captions API: send video, Shorts, channel, playlist or search inputs and get each video's captions back as timed segments (start, end, text) and full text, manual or auto-generated, in the video's own language or any other with timings kept, plus every caption track the video has and its title, views and channel. Call it from code or run it here.
| ๐ฌ Title | ๐ Language | ๐ค Auto | ๐งฉ Segments | ๐ Caption tracks |
|---|---|---|---|---|
| Why the Future is Human โฆ Plus AI | Esther Perel and Sal Khan | English (auto-generated) | yes | 626 | 1 (en (auto)) |
| How to Use Tech to Outsmart Dictators | Leopoldo Lรณpez | TED | English | no | 271 | 4 (ar, en, iw, es) |
| How 180 Strangers Built a Library Together | Rebecca Toh | TED | English | no | 188 | 6 (ar, my, en, iw, โฆ) |
$1.39 per 1,000 transcripts on Starter ($1.59 Free, $1.29 Scale, $1.19 Business). The prefilled run (10 videos) costs about $0.014. Videos without captions are free.
Call it from your code
Start a run and get the captions in one HTTP call (your Apify token from Settings, Integrations):
curl -X POST "https://api.apify.com/v2/acts/deepmine~youtube-captions-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"urls": ["https://youtu.be/iG9CE55wbtY"], "languages": ["en"]}'
Python:
from apify_client import ApifyClientclient = ApifyClient("<APIFY_TOKEN>")run = client.actor("deepmine/youtube-captions-api").call(run_input={"urls": ["https://www.youtube.com/@TED"], "maxVideos": 10, "languages": ["en"]})for video in client.dataset(run["defaultDatasetId"]).iterate_items():print(video["title"], video["transcript"][0])
Each row's transcript is a list of caption lines with start, end and duration in seconds; captionTracks lists every track the video has (language, auto-generated or not). The ๐ฌ Captions tab shows the language, segment count and caption tracks per video. Same engine as YouTube Transcript API, which has every option in one form.
What you get
- The right language. With no language set, you get the video's own spoken language (manual captions first, then auto-generated), never an alphabetically first translation or an auto-dub. A Korean-only video gets its Korean track, and an English talk YouTube auto-dubbed into 20 languages gets English.
isOriginalLanguageon every row says whether the transcript is in the spoken language. - Any language, timings kept. Set Languages and a video without captions in yours is machine-translated segment by segment (Google's engine), so
start/endstay exact and the original text stays inoriginalTranscriptText. Caption tracks people made in your language always come first. Every row says how it got its language:translationSourceishuman,machineoryoutube(empty for the video's own language). No extra cost. - Text plus timings:
transcriptText(plain),transcript(segments withstart,end,durationin seconds andtext), word-level times for auto captions on request, SRT or WebVTT as a ready string. - Video and channel details: title, description, keywords, category, views, likes, the exact comment count, duration, exact publish time (UTC), the countries it plays in, unlisted and family-safe flags, every thumbnail size up to 1280x720, channel name, handle, URL, subscribers, an 800 px channel avatar, verified badge, and every caption track the video has (language, auto or not) plus the languages YouTube lists for auto-translation.
- Many videos at once: single videos, Shorts, a channel's videos, Shorts or live streams, playlists and YouTube searches, each capped by Max videos and optionally by a date range: Published after and Published before (each a date or a period like 7 days; a channel skips its newer videos for free and stops at its first older one). A video listed by several inputs is read and charged once.
- Nothing silent: videos without a transcript (no captions, private, age-restricted, removed) get a free row in the No transcript tab saying why. A "removed" or "unavailable" answer is checked on a second IP first, and when every video comes back without captions the run checks a video known to have them and fails loudly if YouTube's answer has changed.
No login, no cookies, no YouTube API key.
Input
| Field | What it does |
|---|---|
| ๐ฌ YouTube links or video IDs | Videos (watch?v=, youtu.be/, /shorts/, an 11-character ID), channels (@handle, add /shorts or /streams) and playlists |
| ๐ Search YouTube | Optional search words; the top videos are read |
| ๐ Max videos per input | Videos read from each channel, playlist or search; default 20 |
| ๐ Transcript languages | Codes in order of preference (en, es, pt-BR). Empty = the video's own spoken language (never an auto-dub). Set a code to get a dub's or translation's transcript on purpose. Caption tracks people made in your language (common on TED and big channels) are returned whenever they exist; otherwise the captions are translated into the first language (see Translation) |
| ๐ค Translation | Machine translation (default): every segment translated, timings kept, translationSource: machine. If the translator fails or stalls, the row comes in the original language with translationStatus: failed and the reason, and after 3 failures in a row the run stops translating instead of waiting. YouTube's auto-translation: tried once per video, but YouTube refused it for automated downloads on 20 of 21 tries in our tests, so you mostly get the original language; after 5 refusals the run stops asking. None: the original captions |
| ๐ Output format | Plain text + segments (default), plain text only, + SRT, + WebVTT |
| ๐๏ธ Filters | Caption type (manual first, manual only or auto-generated only); Searches return Shorts only; Published after and Published before (channels, playlists and searches: a date like 2026-09-01, that day included, or a period like 7 days; video links are always read) |
| ๐งพ Output | Likes, comments, date and channel stats (on by default, no extra cost); word-level timings |
Ready-made examples
- Get the transcripts of a YouTube channel's latest videos (Y Combinator), with our YouTube Transcript API
- Scrape a YouTube channel's videos, views and likes (@MrBeast), with our YouTube Scraper
- Scrape YouTube search results: the most viewed "AI agents" videos this month, with our YouTube Scraper
Open one and press Try for free: it copies the input to your account, where you change the links or the words.
Use it from an AI agent or the API
Connect the Apify MCP server at https://mcp.apify.com?tools=deepmine/youtube-captions-api (Claude, VS Code or any MCP client), or call the Actor through the Apify API with urls (video links or IDs, Shorts, channels, playlists) and/or searchQueries:
{"urls": ["https://www.youtube.com/watch?v=iG9CE55wbtY", "https://www.youtube.com/@TED"], "maxVideos": 20, "languages": ["en"], "outputFormat": "segments"}
maxVideos caps each channel, playlist or search; languages picks or translates the transcript language; outputFormat is segments (default), text, srt or vtt. Each row has the whole transcript in transcriptText. With neither urls nor searchQueries, the run reads the 3 example videos.
Use cases
- AI and RAG: feed whole transcripts with titles, channels and dates to an LLM, a vector store or a summarizer.
- Subtitles: download SRT or WebVTT files ready for a video editor, or translated subtitles with the original timings.
- Content research: read what a channel or a search says across 20, 200 or 2,000 videos, with views and likes next to each.
- Repurposing: turn talks, podcasts and tutorials into blog posts, notes or quotes with timestamps.
- Language learning and accessibility: the video's own language, or a translation that keeps every line aligned.
Output fields (transcripts, in row order)
thumbnail, title, transcriptSnippet, channelName, videoUrl, language, languageCode, originalLanguageCode and originalLanguage (the video's spoken language), isOriginalLanguage (true when the transcript is in that language, false for a dub or translation), isAutoGenerated, isTranslated, translationSource (human, machine, youtube, or empty for the video's own language), isMachineTranslated, translationStatus (ok, failed, notNeeded when a track in your language exists, notRequested), translationError (why a translation failed; the row is then in the original language), requestedLanguage, viewCount, likeCount, commentCount (exact, from the first comments page; YouTube's rounded figure if that page doesn't answer in time), durationSeconds, duration, publishedAt (exact UTC time; the date alone if YouTube gives only that), publishedTimestamp, category, segmentCount, wordCount, transcriptText, originalTranscriptText (the text before translation on machine-translated rows; null on the others), transcript (unless plain text only), srt / vtt (when chosen), availableLanguages, captionTracks (language code, name, auto-generated or not, translatable or not), translationLanguages, isTranslatable, channelHandle, channelUrl, channelId, channelSubscribers (rounded, as YouTube shows it), channelAvatar (800 px), isChannelVerified, description, descriptionSnippet, keywords, isLive, isPrivate, isUnlisted, isFamilySafe, isShortsEligible, allowRatings, isCrawlable, isUnpluggedCorpus, isOwnerViewing, isTvfilmVideo, uploadedAt, availableCountries, thumbnails, videoId, inputs, scrapedAt (when that row was read). The thumbnail field holds the 1280x720 image when YouTube has one (HD uploads), else 480x360.
No transcript tab (free): thumbnail, title, channelName, videoUrl, hasTranscript (false), status (noCaptions, noMatchingCaptions, private, ageRestricted, notFound, unavailable, liveNotReady, emptyCaptions, blocked), reason, availableLanguages, videoId, inputs, scrapedAt.
Output tabs: ๐ฌ Captions, ๐ Overview (thumbnail, title, transcript snippet, channel, link, language, views, likes, duration, date, words), ๐ Stats (every count), ๐ Languages (language, original or not, auto or not, translated or not, every caption language the video has), ๐ Full Text and ๐ซ No transcript. The run summary (OUTPUT) lists each input with its video count and status.
Measured runs
Apify platform, 1,024 MB (the default), 2026-10-05:
| Input | Transcripts | Free rows (no captions) | Run time | Peak memory |
|---|---|---|---|---|
| TED channel (60 newest) + search "sourdough bread recipe" (60) | 118 | 2 | 14-21 s | 221 MB |
| MrBeast Shorts (40) + a 3Blue1Brown playlist, translated into Spanish, SRT | 48 | 2 | 9-10 s | 208 MB |
| TED channel (100 newest) | 99 | 1 | about 9.5-14 s | 245 MB |
| Veritasium channel (100 newest, ~30 min videos) | 100 | 0 | 22-31 s (3.2-4.5 videos/s) | 237 MB |
Every transcript was charged once (118, 48 and 99 charges). The first row arrived about 3 s after the Actor process started for video links, and 3.6-6.5 s for channels and searches (their video list is read first).
How it gets past YouTube's bot check
YouTube refuses most datacenter IPs and serves the rest without limit, so the run looks for IPs that pass and keeps using them. A video that no datacenter IP will serve (datacenter IPs keep failing, or the video has waited a few seconds for one) is read through a residential IP instead: one small call of a few KB, included in the transcript price. The run goes back to datacenter as soon as one passes again.
On Apify's Free plan your runs get only 5 datacenter IPs (paid plans get 30 or more), and YouTube can refuse all 5. The run then reads videos through residential IPs, enough for a typical small run; a big Free-plan run can still stop with a clear message when that runs out. If YouTube refuses every IP for 5 videos in a row, the run stops and fails with a clear message instead of finishing with empty rows; run it again later (videos already delivered are never charged twice).
Pricing
Pay per transcript: $1.59 per 1,000 on Free, $1.39 Starter, $1.29 Scale, $1.19 Business. No start fee. Videos without a transcript cost nothing. Your run's max cost is never exceeded: the run stops at it.
Feedback
Missing a field or a language? Open an issue on the Actor page. Useful? A quick review helps a lot.