YouTube Transcript Scraper - Captions & SRT avatar

YouTube Transcript Scraper - Captions & SRT

Pricing

from $4.00 / 1,000 transcript fetcheds

Go to Apify Store
YouTube Transcript Scraper - Captions & SRT

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

Heim

Maintained by Community

Actor stats

1

Bookmarked

7

Total users

5

Monthly active users

12 hours ago

Last modified

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 idkaz_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
OutputDataset 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). maxVideosPerChannel lowers that cap.
  • publishedAfter takes a date (2026-10-01) or a rolling window (7 days, 24 hours, 2 weeks); older uploads are skipped and not charged.
  • monitorId remembers every video delivered under that ID (in a key-value store named youtube-transcripts-monitors in 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 monitorId per 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)

  1. Save a Task in Console with your fixed options (languages, includeTimestamps, includeSrt). Agents and cron jobs call the task id, not ad-hoc input.
  2. Schedule the Task (hourly/daily) with channelUrls + a monitorId to transcribe each new upload once, or with a stable youtubeUrls list.
  3. Webhook on SUCCEEDED to your endpoint / Zapier / Make — pull defaultDatasetId and process only type === "transcript" rows.
  4. Cap spend on every automated run with maxTotalChargeUsd. When the cap hits, remaining videos become type: "error" skipped rows — no surprise bill, no silent free transcripts.
  5. 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 --> …"
}
FieldWhen present
transcript, videoId, title, channel, durationSeconds, language, autoGeneratedalways on success
languageFallbacktrue when preferred languages did not match and another track was used
publishedAtupload time (ISO) for channel uploads; null for youtubeUrls inputs
sourceChannelthe channelUrls entry a channel upload came from
availableLanguagesalways on success
segmentsincludeTimestamps: true
srtincludeSrt: true
segmentsUrl / srtUrlrare — 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 + monitorId on 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 transcript by default for RAG / MCP context packing.
  • Language-aware. Prefer your languages list; manual captions beat auto-generated within a language.
  • TikTok Transcripts: TikTok video URLs to transcripts, using captions when present and speech-to-text otherwise.

Pricing

EventPriceWhen
Transcript fetched$0.005Successful caption delivery (one charge per video)
Actor start$0.00005Per 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

  • youtubeUrls and/or channelUrls — 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 or N hours|days|weeks), monitorId (skip videos already transcribed) — channel options above.
  • languages — optional preferred BCP-47 codes in order (default prefers en, then any available).
  • includeTimestamps — adds segments[] 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.