Get YouTube transcripts and subtitles from videos, Shorts, channels, playlists and keyword searches: text, timestamped segments, SRT and VTT, plus views, tags and chapters. No captions? Whisper transcribes the audio. Pick languages and a date window. Failed videos are free.
Versions follow MAJOR.MINOR.PATCH (src/version.py); Apify shows MAJOR.MINOR from .actor/actor.json.
Every run logs its version and records it in the RUN_STATS key-value record.
0.2.4 (2026-10-16)
A run that returns nothing (every link private, removed or blocked, an empty input, a date window that can't
match) now ends SUCCEEDED with an empty dataset and the reason as its status message and in the log, instead of
FAILED. Only an unexpected error still fails the run. RUN_STATS.failure keeps the reason as before.
No change to output fields or price.
0.2.3 (2026-10-15)
Run outside Apify (a local run, as with the other actors), the default input reads YouTube directly instead of
failing at the start for want of an Apify Proxy password. On Apify nothing changes: the proxy setting is used as
before.
No change to output fields or price.
0.2.2 (2026-10-15)
A live stream, or one that has just ended and isn't processed yet, is an answer like a video without captions:
a run of only such links no longer fails. Before, it failed with whisperFallback on (the default) and succeeded
with it off; now both report live with the same reason.
A run that gives only searchQueries no longer also reads, and charges for, the example video Apify fills into
urls by default (an API call that leaves urls out). A link you give yourself is still read.
No change to output fields or price.
0.2.1 (2026-10-14)
Caption tracks of very long videos (a 10-hour course: over 10 MB of json3) are read; they failed at the shared
10 MB body cap before (cap now 80 MB; the tracks come from the run's own address, not the proxy).
An item that would pass Apify's 9 MB dataset item limit leaves out WebVTT, then SRT, then the segments, keeping the
text, and says so in notes (a 10-hour course with every format on is ~6 MB).
0.2.0 (2026-10-14)
Search keywords (searchQueries) and YouTube search result links in urls: the top results in YouTube's
order, up to maxVideosPerSource each (inputType: "search").
Date window (publishedAfter, publishedBefore: a day or a span such as "30 days") for videos from channels,
playlists and searches. A list's rough dates skip videos certainly outside it before they're read, a channel's list
ends at its first video older than the window, and each video's exact day decides; skipped videos are free
(outsideWindow in RUN_STATS). Pasted video links are always read.
Language list: language takes several codes in order of preference (es, en).
maxVideosPerSource up to 5,000 (was 1,000).
New fields: publishedAt (exact UTC publish time) and availability (public or unlisted).
About half the proxy traffic per video: one anonymous visitor id per run (YouTube's service-worker data, ~3 KB)
lets each video be read from YouTube's API without the watch page, and YouTube's player script (~760 KB, needed
only for media links) is never downloaded for a transcript. Any light read that goes wrong is redone the full way
at once, so it can't cost a transcript (lightReads, lightFallbacks in RUN_STATS).
Every request is pinned to English (hl=en, Accept-Language: en-US): residential addresses are worldwide and
YouTube localizes by address. Numbers and dates come from YouTube's fixed fields.
A bot check that yt-dlp reports only as a warning (a read without media links) is a bot check again: retried from a
new address and reported as blocked, never as "no captions".
Not added: translation. YouTube answered every machine-translated caption request (tlang) with HTTP 429, direct and
through residential addresses, in json3, srv3 and vtt (2026-10-14, 12 tries on 4 videos).
0.1.0 (2026-10-14)
First release.
Transcripts of YouTube videos, Shorts, channels (@handle, /channel/, /c/, /user/, a /videos, /shorts
or /streams tab) and playlists, newest first per channel or playlist (maxVideosPerSource), each video once.
Caption choice: the uploader's subtitles first, then YouTube's automatic captions of the spoken language (the
"-orig" track), never a machine translation; language picks a language, with a flagged fallback to the
video's own.
Videos without captions are transcribed from their smallest audio stream with Whisper (faster-whisper base or
small, the engine and worker of audio-transcriber), charged per started audio minute (whisper-minute,
whisper-minute-accurate) after the transcript is in the dataset; capped by maxWhisperMinutesPerVideo and by
the maximum cost per run.
Charged per transcript through Apify's apify-default-dataset-item event; videos without a transcript are not
written or charged; each is listed with its reason in RUN_STATS.
Whisper gets a time budget per video (30 s per audio minute at 4 GB, 60 s at the default 2 GB, 3x for small);
a transcript cut there says so in notes and only its minutes are charged.
Apify's residential proxy by default (datacenter addresses got a bot check on every try); caption tracks are
fetched without the proxy first (their links aren't tied to an address), through it if refused.
A bot check or HTTP 429 is retried twice from a new proxy IP. One failing video or input line never affects the
others.
Sources and terms (ACTOR-CHECKLIST 0 and 6), read 2026-10-14:
Data: youtube.com's public, logged-out video pages, channel and playlist pages and caption tracks, read with
yt-dlp (https://github.com/yt-dlp/yt-dlp ), the maintained open-source client for them; caption tracks fetched
through mms_common from www.youtube.com only. No official API serves other people's captions (the YouTube Data API
captions.download needs the video owner's authorization).
robots.txt: https://www.youtube.com/robots.txt disallows /api/ (caption tracks) and /youtubei/ (the player
and list API) for every crawler; googlevideo.com's (the audio) disallows everything. Read under Michael's platform
exception of 2026-10-14 (ACTOR-CHECKLIST 2b, robots_exempt_hosts).
Terms: https://www.youtube.com/t/terms forbids "access[ing] the Service using any automated means (such as robots,
botnets or scrapers) except (a) in the case of public search engines, in accordance with YouTube's robots.txt
file; or (b) with YouTube's prior written permission". Michael's decision (2026-10-14) is to read the logged-out
public data anyway, like the Store's leading actors; the README says so plainly and offers takedown on request.