YouTube Transcript Scraper - Captions & SRT
Pricing
from $4.00 / 1,000 transcript fetcheds
YouTube Transcript Scraper - Captions & SRT
YouTube transcript scraper: extract video captions (manual or auto-generated), timestamps, SRT subtitles, multi-language — via API, MCP, or schedule. $0.005 per video. One JSON row per URL. No browser, no API key.
Pricing
from $4.00 / 1,000 transcript fetcheds
Rating
0.0
(0)
Developer
Heim
Maintained by CommunityActor stats
1
Bookmarked
7
Total users
5
Monthly active users
12 hours ago
Last modified
Categories
Share
YouTube Transcripts — Captions from Videos & Channels
URL or channel in → transcript out. Pass YouTube watch/shorts/youtu.be links or channel links (@handle); get one JSON dataset row per video with the caption track (manual or auto-generated). No API key, no browser. Built for MCP agents, API clients, and scheduled pipelines.
| Actor id | kaz_kakyo/youtube-transcripts |
| Minimal input | { "youtubeUrls": ["https://www.youtube.com/watch?v=…"] } |
| Channel input | { "channelUrls": ["https://www.youtube.com/@…"], "monitorId": "my-channels" } — new uploads only, on a schedule |
| Cost | $0.005 per video · $0.00005/run start |
| Output | Dataset rows with type: "transcript" or type: "error" |
Call it (MCP / API / schedule)
MCP (agents)
{"actor": "kaz_kakyo/youtube-transcripts","input": {"youtubeUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"]}}
Optional extras agents usually want:
{"youtubeUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],"languages": ["en"],"includeTimestamps": true,"includeSrt": true}
After the run, read the default dataset. Every row has a type discriminator — filter on "transcript"; treat "error" as per-video failure. Missing captions, private/age-restricted/region-blocked videos, and bad URLs become error rows and the run still SUCCEEDS (including all-failed batches) so agent mistakes do not look like platform outages. The run fails only on a post-charge delivery failure (charged but could not write the dataset).
Transcribe a channel's new uploads
{"channelUrls": ["https://www.youtube.com/@veritasium", "https://www.youtube.com/channel/UC…"],"publishedAfter": "7 days","monitorId": "science-channels-daily"}
- Each channel contributes its most recent uploads, newest first — up to 15 (YouTube's public uploads feed).
maxVideosPerChannellowers that cap. publishedAftertakes a date (2026-10-01) or a rolling window (7 days,24 hours,2 weeks); older uploads are skipped and not charged.monitorIdremembers every video delivered under that ID (in a key-value store namedyoutube-transcripts-monitorsin your account) and skips it on later runs, free. Schedule the same input daily and you pay only for new uploads. A video whose captions are not ready yet (brand-new uploads can take a few hours) is retried on the next run.- Use one
monitorIdper schedule: two runs overlapping on the same ID can both transcribe — and charge for — the same new upload. There is no cross-run exactly-once guarantee. - Need older videos than the latest 15? Pass their URLs in
youtubeUrls(e.g. from any channel scraper).
API / apify-client
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('kaz_kakyo/youtube-transcripts').call({ youtubeUrls: ['https://www.youtube.com/watch?v=jNQXAC9IVRw'], includeTimestamps: true },{ maxTotalChargeUsd: 1.0 }, // hard budget for this run);const { items } = await client.dataset(run.defaultDatasetId).listItems();const transcripts = items.filter((i) => i.type === 'transcript');
Same shape via REST: POST /v2/acts/kaz_kakyo~youtube-transcripts/runs with your token, then poll or attach a webhook.
Make it recurring (what sticky callers do)
- Save a Task in Console with your fixed options (
languages,includeTimestamps,includeSrt). Agents and cron jobs call the task id, not ad-hoc input. - Schedule the Task (hourly/daily) with
channelUrls+ amonitorIdto transcribe each new upload once, or with a stableyoutubeUrlslist. - Webhook on
SUCCEEDEDto your endpoint / Zapier / Make — pulldefaultDatasetIdand process onlytype === "transcript"rows. - Cap spend on every automated run with
maxTotalChargeUsd. When the cap hits, remaining videos becometype: "error"skipped rows — no surprise bill, no silent free transcripts. - Chain: any YouTube search/channel scraper that outputs video URLs → this actor (for back catalogues beyond the latest 15 uploads). For speech-to-text of arbitrary media files (not YouTube captions), use kaz_kakyo/audio-transcriber with a direct file URL instead.
Long runs checkpoint finished video IDs — a platform migration resumes without re-billing completed videos.
Output contract
One dataset item per input URL or selected channel upload (plus skipped/invalid rows). Success shape:
{"type": "transcript","url": "https://www.youtube.com/watch?v=jNQXAC9IVRw","videoId": "jNQXAC9IVRw","title": "Me at the zoo","channel": "jawed","channelId": "UC…","durationSeconds": 19,"publishedAt": null,"language": "en","languageName": "English","autoGenerated": false,"languageFallback": false,"transcript": "Full caption text…","availableLanguages": [{ "language": "en", "name": "English", "autoGenerated": false }],"segments": [{ "start": 0.0, "duration": 2.1, "text": "…" }],"srt": "1\n00:00:00,000 --> …"}
| Field | When present |
|---|---|
transcript, videoId, title, channel, durationSeconds, language, autoGenerated | always on success |
languageFallback | true when preferred languages did not match and another track was used |
publishedAt | upload time (ISO) for channel uploads; null for youtubeUrls inputs |
sourceChannel | the channelUrls entry a channel upload came from |
availableLanguages | always on success |
segments | includeTimestamps: true |
srt | includeSrt: true |
segmentsUrl / srtUrl | rare — oversized payloads spilled to the key-value store |
Failure / skip row (never charged):
{ "type": "error", "url": "https://…", "videoId": "…", "error": "…", "errorCode": "captions_disabled" }
Channel and input errorCodes: invalid_channel_url, channel_not_found, channel_fetch_failed, already_transcribed (skipped for your monitorId), invalid_input (bad publishedAfter / monitorId — nothing is fetched).
Download the dataset as JSON, CSV, Excel, or HTML from Console or the dataset API.
Why this one
- Cheap captions path. $0.005 per video, the same price on every Apify plan. Near-zero compute — HTTP only, no browser, no Whisper.
- Channel monitoring built in.
channelUrls+monitorIdon a schedule = each new upload transcribed and billed once. - Agent-safe. Bad/missing-caption URLs become
type: "error"rows; the run still succeeds. - Timestamps + SRT when you need them; plain
transcriptby default for RAG / MCP context packing. - Language-aware. Prefer your
languageslist; manual captions beat auto-generated within a language.
Related actors
- TikTok Transcripts: TikTok video URLs to transcripts, using captions when present and speech-to-text otherwise.
Pricing
| Event | Price | When |
|---|---|---|
| Transcript fetched | $0.005 | Successful caption delivery (one charge per video) |
| Actor start | $0.00005 | Per run |
Error / skipped rows, channel lookups and videos already transcribed for your monitorId are never billed. Cap spend with maxTotalChargeUsd on the run or task.
Input rules agents must follow
youtubeUrlsand/orchannelUrls— at least one is needed. Video URLs:watch/shorts/youtu.be/embed/live, or bare 11-char video IDs. Channels:youtube.com/@handle,/channel/UC…,/c/…,/user/…(max 100 per run). Max 500 videos per run in total; duplicates are collapsed by video ID.maxVideosPerChannel(1–15, default 15),publishedAfter(date orN hours|days|weeks),monitorId(skip videos already transcribed) — channel options above.languages— optional preferred BCP-47 codes in order (default prefersen, then any available).includeTimestamps— addssegments[]with{ start, duration, text }.includeSrt— adds an.srt-format string.
Videos with captions disabled, private/deleted, age-restricted, or region-blocked return error rows. This actor fetches existing YouTube captions — it does not run speech-to-text. For arbitrary audio/video file URLs, use kaz_kakyo/audio-transcriber.
See the Input tab for the full schema. See the API tab for run/dataset endpoints.
FAQ
Do I need a YouTube API key? No. The actor uses the public Innertube player + timedtext endpoints.
Manual vs auto captions? Within a preferred language, manual tracks are chosen over auto-generated (autoGenerated: true when ASR was used).
Why did my URL fail? No public captions, or the video is private / age-gated / region-blocked. Check error / errorCode on the dataset row — the run status will still be SUCCEEDED.
How do I keep costs predictable on a schedule? Set maxTotalChargeUsd on the run/task, and use monitorId so each video is billed once.
Can I get a channel's whole back catalogue? Channel input covers the latest 15 uploads. For older videos, pass their URLs in youtubeUrls.
If this saved you time, a Store review on the actor page helps a solo dev. Hit a problem? Open an issue.
Telemetry
Each run records one pseudonymous event: a salted hash of the caller account ID (never the raw ID), run origin (Console / API / MCP / …), and a timestamp. No inputs, results, or personal data — adoption measurement only.