AS
Apify Store
/johnvc/YoutubeTranscripts
YouTube Transcript & Subtitles Scraper API
Scrape YouTube transcripts, subtitles, and captions in bulk, the cheapest pay-per-video YouTube transcript API on Apify. Callable from any MCP client (Claude, Cursor, ChatGPT). Supports YouTube videos, Shorts, and every URL format.
liveasyncunknown~30s typical
VIDEOSAIDEVELOPER_TOOLS
$0.0020
per result + $0.01 call
plus $0.001 platform fee per run
Usage-based actor: event charges passed through at cost plus 25%
Bring your own Apify Store key: provider portion drops to $0.
$Set up https://cracked-ai-pearl.vercel.app/SKILL.md, then use Cracked to run apify /johnvc/YoutubeTranscripts for me.
Input
| Field | Type | Description |
|---|---|---|
| youtube_url | any | Required unless 'channel' is provided. Provide one YouTube URL as a string, or multiple as an array (max 250 URLs per run). Works with standard videos, Shorts, youtu.be short links, embed URLs, and m.youtube.com mobile URLs. Channel URLs (youtube.com/@handle, /channel/, /c/, /user/) are detected automatically and treated as the 'channel' input. Each video URL is processed in parallel. If more than 250 URLs are submitted, only the first 250 are processed and a truncation notice is written to the dataset - split larger batches into multiple runs. Invalid URLs are recorded as errors in the dataset but do not stop the run. Charged per video successfully transcribed. |
| languages | array | Ordered list of ISO 639-1 language codes. The first available transcript matching one of these languages is returned. Example: ["en", "es", "fr"] tries English first, then Spanish, then French. Defaults to ["en"] when omitted. default ["en"] |
| translate_to | string | Optional. If set to an ISO 639-1 code (e.g. "es", "fr", "de"), the picked transcript is translated to this language. Requires the source transcript to be translatable - run with list_only=true first to see translation_languages for each available transcript. If translation is unavailable, the original transcript is returned (no error). |
| transcript_type | string (any | manual | generated) | Filter which transcript variant to consider when both manually-created and auto-generated captions exist for a language. "any" picks whichever matches the language priority first; "manual" only considers human-made captions; "generated" only considers auto-generated. default "any" |
| output_formats | array | Optional list of extra output formats to include in the dataset alongside the default structured fields. Allowed values: "srt" (SubRip subtitles), "vtt" (WebVTT subtitles), "text" (plain text, newline-separated). Unknown values are ignored with a warning. The structured fields (timestamped, non_timestamped, etc.) are always included. default [] |
| preserve_formatting | boolean | If true, keep YouTube's inline HTML formatting tags (<i>, <b>) in transcript text. Default false (strips them for clean text). default false |
| list_only | boolean | If true, do NOT fetch any transcript. Instead, for each URL return the list of all available transcripts with their language code, generated/manual status, is_translatable flag, and translation_languages. Useful for discovering what languages exist before picking one. Not charged as a videoprocessed event. default false |
| include_metadata | boolean | If true, also fetch video metadata via yt-dlp: title, description, channel_name, channel_id, channel_url, view_count, like_count, video_duration_seconds, upload_date, thumbnail_url, tags, categories, availability, was_live. Adds about 1-3 seconds per video. If metadata extraction fails, the transcript is still returned and metadata_error is populated. Defaults to true. default true |
| channel | any | Optional. One YouTube channel as a string, or multiple as an array. Accepts an @handle ("@mkbhd"), a bare handle ("mkbhd"), a channel ID ("UCBJycsmduvYEL83R_U4JriQ"), or any channel URL (/channel/, /c/, /user/, /@handle; a /videos, /shorts, or /streams tab URL keeps that tab). Each channel produces one dataset row per video, newest first, up to max_videos: video_id, url, title, duration, view count, and thumbnail. Rows are marked result_type="channel_video". Listing is not charged as a videoprocessed event. |
| max_videos | integer | Maximum number of videos to list per channel, newest first. Default 100. Values above 1000 are capped at 1000 (and values below 1 are raised to 1) with a log warning rather than failing the run. When channel_transcripts is true, at most 250 videos per run (across all channels plus explicit URLs) proceed to transcript extraction. default 100 |
| channel_transcripts | boolean | If true, every video listed from the channel(s) is also queued for transcript extraction (newest first, deduplicated against youtube_url entries, subject to the 250 videos per run limit). Each successful transcript is charged as a videoprocessed event, exactly like videos passed via youtube_url. Default false: channels are only listed. default false |
Call it
curl
curl https://cracked-ai-pearl.vercel.app/v1/run \
-H "Authorization: Bearer ck_live_..." -H "content-type: application/json" \
-d '{"provider":"apify","endpoint":"/johnvc/YoutubeTranscripts","input":{"languages":["en"],"transcript_type":"any","output_formats":[],"preserve_formatting":false,"list_only":false,"include_metadata":true,"max_videos":100,"channel_transcripts":false},"wait":false}'poll
curl "https://cracked-ai-pearl.vercel.app/v1/runs/<runId>?wait=30" -H "Authorization: Bearer ck_live_..."cli
npx cracked-ai run -p apify -e /johnvc/YoutubeTranscripts -i '{"languages":["en"],"transcript_type":"any","output_formats":[],"preserve_formatting":false,"list_only":false,"include_metadata":true,"max_videos":100,"channel_transcripts":false}' -w 120mcp
run_tool({ provider: "apify", endpoint: "/johnvc/YoutubeTranscripts", input: {"languages":["en"],"transcript_type":"any","output_formats":[],"preserve_formatting":false,"list_only":false,"include_metadata":true,"max_videos":100,"channel_transcripts":false}, wait: false })Try it
Runs against your signed-in workspace balance. Sign in if you have not.