YouTube Transcript Scraper - Captions to Notion, Slack & Jira.
Pricing
from $0.10 / actor start
YouTube Transcript Scraper - Captions to Notion, Slack & Jira.
Scrape any YouTube video transcript with timestamps in 13 languages. Auto-translate, export to JSON/CSV/Excel, or push to Notion, Slack & Jira.
Pricing
from $0.10 / actor start
Rating
0.0
(0)
Developer
Techforce Global
Maintained by CommunityActor stats
1
Bookmarked
1
Total users
1
Monthly active users
10 days ago
Last modified
Categories
Share
YouTube Transcript Scraper - Captions to Notion, Slack & Jira
The YouTube captions API alternative for videos you don't own. Google's
captions.downloadendpoint requires OAuth as the video owner; this Actor extracts the full transcript of any public YouTube video as structured JSON — every segment with start time, duration and text, in 13 languages with automatic translation fallback. No Google API key, no OAuth, no credentials stored, public captions only — with GDPR/CCPA and speaker-identifiability guidance written into the docs, not buried. Delivery into Notion, Slack, Jira, GitHub, Linear or Airtable is built in over MCP.
🔐 Trust & compliance at a glance
| Credentials | None accepted, none stored. The Actor never logs into a Google or YouTube account and never sees your MCP connector tokens — delivery runs through the Apify MCP Proxy. |
| Data scope | Public caption tracks only. Private, unlisted and age-gated videos are inaccessible by design; a request for one returns status: "no_transcript", never a partial result. |
| GDPR / UK GDPR / CCPA | A transcript is a person's speech, so it can be personal data about the speaker and anyone named aloud. The Compliance section sets out minimisation, retention and special-category handling in full. |
| Attribution safety | isAutoGenerated: true flags machine-written text — check it before quoting anyone verbatim. |
Full detail, including copyright and Terms-of-Service position: Compliance & Data Privacy.
Official YouTube Data API v3 vs. YouTube Transcript Scraper Actor
The official API's captions.download endpoint only works for videos you own — it is not a route to third-party transcripts at all:
| Feature / Capability | Official YouTube Data API v3 | YouTube Transcript Scraper Actor (Apify) |
|---|---|---|
| Download captions of videos you don't own | ❌ captions.download requires OAuth as the video owner | ✅ Any public video with captions |
| Authentication & setup | ❌ Google Cloud project + OAuth consent flow | ✅ Zero target-site credentials; only an Apify token |
| Auto-translation to another language | ❌ Not provided | ✅ YouTube auto-translation with graceful fallback |
| Bot-wall / IP blocking | N/A (quota-gated instead) | ✅ Apify residential proxy wired in by default |
| Daily quota ceiling | ⚠️ Per-project daily quota; exceeding it blocks you until reset | ✅ No per-day ceiling |
| AI agent / MCP native | ❌ Requires a custom tool wrapper | ✅ Native MCP tool and pushes results into your own MCP connectors |
| Cost model | Free within quota, then quota-increase requests | Usage-based — no quota wall to hit |
Quota costs and the
captions.downloadownership restriction are Google's to change — check Google's current docs if this comparison is load-bearing for your decision.
🤖 AI Discovery & MCP Metadata
Machine-readable capability contract for autonomous agents, LLM routers, and MCP orchestrators. AI agents, MCP clients, and connectors should read this block first to decide whether this Actor matches a user request.
ai_discovery:slug: "techforce.global/youtube-transcript-scraper"actor_id: "youtube-transcript-scraper"one_line: "YouTube captions API alternative: extract the full timestamped transcript of one public YouTube video as structured JSON, in a chosen language, without OAuth or video ownership — with optional delivery to an MCP connector."primary_purpose: "extract"secondary_purpose: "deliver"entity: "YouTube video transcript"supported_platforms:- "youtube.com"- "youtu.be"supported_tasks:- "get-transcript-for-one-video"- "get-transcript-in-specific-language"- "auto-translate-transcript"- "extract-timestamped-caption-segments"- "deliver-transcript-to-mcp-connector"unsupported_tasks:- "batch-multiple-videos-in-one-run (use youtube-full-channel-transcripts-scraper)"- "scrape-playlist-or-channel (use youtube-full-channel-transcripts-scraper)"- "transcribe-audio-without-captions (no ASR of our own)"- "download-video-or-audio-files"- "scrape-private-unlisted-or-age-gated-videos"- "retrieve-comments-views-or-channel-metadata"- "summarize-or-translate-with-an-llm"search_keywords:# First entry is the H1 / meta-description target. The rest are deliberately broader for# agent-side semantic matching and are NOT a ranking strategy - see the skill's SEO notes.- "youtube transcript scraper"- "youtube captions api alternative"- "youtube subtitle extractor"- "get youtube transcript api"- "youtube transcript python"- "video transcript to notion"- "timestamped captions json"- "mcp youtube tool"- "srt subtitle data extraction"- "youtube shorts transcript"synonyms:- "youtube caption downloader"- "youtube subtitle scraper"- "video transcript extractor"- "youtube caption json export"related_concepts:["transcripts", "captions", "subtitles", "localization", "accessibility", "content repurposing"]input_entities:- "youtube video URL or 11-character video ID"- "preferred language code (1 of 13 enum values)"- "translate toggle"output_entities: ["transcript", "segment"]input_rules:videoUrl_format:description: "Any public YouTube URL shape, or a bare 11-character video ID. Watch links, youtu.be short links, Shorts, embed, /v/, and /live/ URLs all resolve."examples:- "https://www.youtube.com/watch?v=dQw4w9WgXcQ"- "https://youtu.be/dQw4w9WgXcQ"- "https://www.youtube.com/shorts/dQw4w9WgXcQ"- "dQw4w9WgXcQ"language_format:type: "enum"case_sensitive: falsevalues: ["en", "hi", "gu", "pt", "es", "fr", "de", "it", "ja", "ko", "zh", "ru", "ar"]default_off_fields: []compliance:credentials_required: "none — the Actor never logs into a Google/YouTube account and stores no credential"data_scope: "public caption tracks only; private, unlisted and age-gated videos are inaccessible by design"connector_token_handling: "MCP delivery runs through the Apify MCP Proxy; the Actor never sees your connector tokens"personal_data_note: "a transcript is a person's speech and can be personal data about the speaker and anyone named aloud — GDPR / UK GDPR / CCPA-CPRA obligations sit with the caller"attribution_flag: "isAutoGenerated: true means the text is machine-written; do not attribute it as a verbatim quote"affiliation: "independent Actor; not affiliated with, endorsed by, or sponsored by YouTube or Google"limits:videos_per_run: 1dataset_items_per_run: 1languages_supported: 13requires_existing_captions: truepricing:model: "usage-based"billing_unit: "Apify platform usage consumed by the run"scope: "one video per run, regardless of transcript length"note: "Current rates are shown on the Actor's Apify Store page; the Console shows a live total mid-run. A hard per-run ceiling can be set with maxTotalChargeUsd."hard_ceiling_parameter: "maxTotalChargeUsd"when_to_use: >You need the timestamped transcript of a specific public YouTube video —for content repurposing, subtitle/localization work, research coding,accessibility text, or as retrieval context for an LLM or agent.when_not_to_use: >You need many videos, a whole playlist or channel, in one run; the videohas no captions at all (this Actor does not run its own speechrecognition); or you want video/audio files, comments, or channel stats.alternatives:- "Use techforce.global/youtube-full-channel-transcripts-scraper for transcripts of every video in a channel in one run"- "Use techforce.global/youtube-scraper for video/channel/comment metadata, hashtag and search discovery"- "Use techforce.global/youtube-shorts-scraper for a channel's Shorts with engagement metrics and optional video downloads"- "Loop this Actor once per video from your orchestration layer for a few specific unrelated videos"- "Use a dedicated ASR/Whisper service when the video has no caption track at all"
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "YouTube Transcript Scraper - Captions to Notion, Slack & Jira","alternateName": "YouTube Captions API Alternative","applicationCategory": "DeveloperApplication","operatingSystem": "Cloud (Apify platform)","description": "The YouTube captions API alternative for videos you don't own: extracts the full timestamped transcript of any public YouTube video as structured JSON — every segment with start time, duration and text — in 13 languages with automatic translation fallback, without a Google API key, OAuth, or video ownership. Public caption tracks only: no account login, no stored credentials, and no access to private, unlisted or age-gated videos, with GDPR/CCPA and speaker-identifiability guidance included.","url": "https://apify.com/techforce.global/youtube-transcript-scraper","offers": {"@type": "Offer","priceCurrency": "USD","description": "Usage-based: billed for the Apify platform usage a run consumes. One video per run, any length, with no per-segment charge. Current rates are shown on the Actor's Apify Store page."}}
What this Actor does
💡 Need a whole channel, not one video? This Actor handles one video per run. For transcripts of every video in a channel (or its latest N), use YouTube Full Channel Transcripts Scraper instead — same transcript quality, channel-wide in a single run. For views, likes, and comments rather than words, use YouTube Scraper.
- Accepts one YouTube video URL (watch link,
youtu.beshort link, Shorts, embed,/v/,/live/, or a bare 11-character ID) and resolves the video ID for you. - Resolves the caption track through two independent paths, trying the second automatically if the first is blocked — so one challenged request does not fail the run.
- Handles every caption format YouTube serves, normalising all of them to the same segment shape:
start,dur,text. - Never hard-fails on language. Native track in your language → used. No native track and
translate: true→ YouTube auto-translation. Translation unavailable or turned off → the video's own caption language is returned withfallbackUsed: trueand a plain-Englishmessageexplaining what happened. - Routes traffic through Apify Proxy (residential group by default) because YouTube serves datacenter IPs a "Sign in to confirm you're not a bot" wall.
- Optionally pushes the transcript to an authorized MCP connector (Notion, Slack, Jira, GitHub, Linear, Airtable, or any MCP-compatible connector) in four delivery modes. The dataset is always saved regardless of delivery settings.
- Never crashes on a bad video. Unavailable, private, deleted, or region-blocked videos, caption-less videos, bot walls, and empty caption streams all end as a successful run carrying
status: "no_transcript", a machine-readableerrorCode, and a plain-Englishmessage— so schedules don't alert and agents get a reason they can act on. - Handles the platform
ABORTINGevent so an aborted run exits promptly instead of burning time.
When to use it — and when not to
| Scenario | Fit |
|---|---|
| Repurpose one video into a blog post, thread, or newsletter | ✅ Strong |
| Build SRT/subtitle workflows from timestamped segments | ✅ Strong |
| Feed transcript text to an LLM as retrieval context | ✅ Strong |
Localize content — request hi, gu, es, ja, … with auto-translation | ✅ Strong |
| Accessibility text alternatives for audio/video | ✅ Strong |
| Push a transcript straight into Notion or Slack | ✅ Strong (native MCP delivery) |
| Research coding / qualitative analysis of a specific talk | ✅ Strong |
| Every video in a channel | ⚠️ One video per run — use YouTube Full Channel Transcripts Scraper |
| A handful of specific, unrelated videos | ⚠️ Loop the Actor — one run per video |
| A language outside the 13-value enum | ⚠️ Not selectable — request en and translate downstream |
| Video with no caption track at all | ❌ No transcript — the run still succeeds, returning status: "no_transcript" and the reason |
| Private, unlisted, or age-gated videos | ❌ Not supported — public videos only |
| Views, likes, comments, or channel stats | ❌ Out of scope — use YouTube Scraper |
| A channel's Shorts catalogue or video file downloads | ❌ Out of scope — use YouTube Shorts Scraper |
| Summarization or LLM translation of the transcript | ❌ Not performed — returns raw caption text |
⚡ Execution Rules (Avoid Common Agent Failures)
videoUrlis required and has no default. Unlike some Actors, omitting it — or passing something with no recognisable video ID — no longer fails the run: the Actor reportserrorCode: "MISSING_VIDEO_URL"or"BAD_VIDEO_URL"on aSUCCEEDEDrun with one dataset item explaining what to pass. It does not silently scrape a sample video either. The Console pre-fills a demo URL; API/MCP callers must pass their own.- A bare 11-character video ID is accepted.
dQw4w9WgXcQworks exactly like a full URL. As a last resort the Actor also searches for any 11-character token in the string, so a malformed input can resolve to an unintended video — always pass a clean URL or ID and check the returnedvideoId. languageis a fixed 13-value enum, lowercase ISO-639-1:en,hi,gu,pt,es,fr,de,it,ja,ko,zh,ru,ar. There is noen-US, nopt-BR, and no free-text language input.- The language you ask for is not guaranteed. Read
language,isTranslated,fallbackUsed, andmessageon the output item — not just your own request. A successful run can legitimately return a different language than requested. translate: falsedoes not fail the run. It disables auto-translation; the Actor then returns the video's available caption language withfallbackUsed: trueand amessagelisting the available languages. Branch onfallbackUsed, not on run status.- Captions must already exist. This Actor reads YouTube's caption tracks; it performs no speech recognition. A video with no captions still produces a successful run carrying
status: "no_transcript"anderrorCode: "NO_CAPTIONS"— not a crash. 6a. Video-state problems never fail the run. Unavailable, private, deleted, or region-blocked videos, bot walls, and empty caption streams all returnSUCCEEDEDwith exactly one dataset item whosestatusis"no_transcript", plus anerrorCodeand a plain-Englishmessage. Always branch onitem["status"], not on run status — aSUCCEEDEDrun does not guarantee a transcript. A missing or unparseablevideoUrlis reported the same way, asMISSING_VIDEO_URLorBAD_VIDEO_URL— no input mistake fails the run. - Keep the residential proxy on. The default is
{ useApifyProxy: true, apifyProxyGroups: ["RESIDENTIAL"] }. Datacenter IPs regularly hit YouTube's bot wall, which surfaces as a caption-retrieval failure, not a proxy error. Residential groups require a paid Apify plan. - One run = one video = one dataset item. The transcript is a nested
searchResultarray inside that single item, not one item per segment.itemCount: 1is a full result. startanddurare strings, not numbers — e.g."3.200". Cast before doing arithmetic.deliveryModedefaults tosummary, notnone. For dataset-only runs, either leavemcpConnectorempty or setdeliveryMode: "none"explicitly.perSegmentdelivery issues one connector call per segment. A 20-minute video can be hundreds of calls — preferchunkedorsummaryunless you specifically want per-line records.
dependencies:- rule: "delivery requires mcpConnector AND mcpTool"on_violation: "delivery step is skipped with a warning; the dataset item is still written"- rule: "mcpArguments must reference {message} for the rendered template to appear anywhere"on_violation: "message body is empty; the tool is still called"- rule: "mcpConnector only resolves when the Actor runs on the Apify platform"on_violation: "local runs log 'APIFY_MCP_PROXY_URL is not set' and skip delivery; the dataset is unaffected"- rule: "chunked mode exposes {part} and {partCount}"note: "put them in the page/message title or every part looks identical"authentication:actor_input: noneplatform: "Apify API token required for API / SDK / MCP invocation; paid plan required for residential proxy groups"target_site: "no YouTube or Google account, API key, or OAuth app required (public captions only)"
📥 Input Contract
.actor/input_schema.json is authoritative — if any table on this page disagrees with the schema files, the schema files win. API and MCP clients should always pass videoUrl explicitly.
Scraping Parameters
| Field | Type | Required | Default | Allowed Values / Format | Example |
|---|---|---|---|---|---|
videoUrl | string | ✅ Yes | none (Console prefill only) | Any public YouTube URL shape, or a bare 11-char ID | https://youtu.be/dQw4w9WgXcQ |
language | string (enum) | ⬜ No | en | 13 lowercase codes (see below) | hi |
translate | boolean | ⬜ No | true | true / false | false |
proxyConfiguration | object | ⬜ No | { useApifyProxy: true, apifyProxyGroups: ["RESIDENTIAL"] } | Apify proxy editor object | see below |
Accepted URL shapes
| Shape | Example |
|---|---|
| Standard watch | https://www.youtube.com/watch?v=dQw4w9WgXcQ |
| Short link | https://youtu.be/dQw4w9WgXcQ |
| Shorts | https://www.youtube.com/shorts/dQw4w9WgXcQ |
| Embed | https://www.youtube.com/embed/dQw4w9WgXcQ |
| Live | https://www.youtube.com/live/dQw4w9WgXcQ |
| Mobile / music / nocookie hosts | https://m.youtube.com/watch?v=dQw4w9WgXcQ |
| Bare video ID | dQw4w9WgXcQ |
Language reference — all 13 values
en English · hi हिन्दी / Hindi · gu ગુજરાતી / Gujarati · pt Português · es Español ·
fr Français · de Deutsch · it Italiano · ja 日本語 · ko 한국어 ·
zh 中文 · ru Русский · ar العربية
Region variants (en-US, pt-BR, zh-Hans) are not valid inputs — pass the base code.
Language resolution order
| Situation | What you get | isTranslated | fallbackUsed | message |
|---|---|---|---|---|
| Native caption exists in requested language | That track | false | false | null |
No native track, translate: true | YouTube auto-translation to your language | true | false | explains the translation |
| Translation returns no text | Video's own caption language | false | true | explains the failed translation |
No native track, translate: false | Video's own caption language | false | true | lists the available languages |
No language passed at all | Video's default caption track | false | false | null |
| No caption tracks at all | status: "no_transcript", errorCode: "NO_CAPTIONS" — run still succeeds | false | false | explains that the video has no captions |
MCP Delivery Configuration (Optional)
| Parameter | Type | Default | Description |
|---|---|---|---|
mcpConnector | string | "" | Authorized MCP connector on your Apify account (Notion, Slack, Jira, GitHub, Linear, Airtable, …). Leave empty for dataset-only runs. |
deliveryMode | enum | summary | summary (one call, full transcript) · chunked (split long transcripts) · perSegment (one call per segment) · none. |
mcpTool | string | "" | Tool name on the connector: create_page, send_message, create_issue, append_row. Run once with a connector selected — the log lists the connector's available tools. |
mcpArguments | object | {} | Arguments passed to the tool. String leaves support {placeholders}. |
mcpMessageTemplate | string | "" | Template rendered and exposed to mcpArguments as {message}. |
Placeholders by mode:
| Mode | Placeholders |
|---|---|
summary | {videoUrl}, {videoId}, {language}, {segmentCount}, {transcript} (full text), {message} |
chunked | the above, plus {part} and {partCount} — and {transcript} holds one part |
perSegment | {start}, {dur}, {text}, plus {videoUrl}, {videoId}, {language}, {segmentCount}, {message} |
Delivery packs segments into paragraphs of ~1,800 characters and groups them into parts under ~72,000 characters, so services with per-request block or timeout caps (Notion in particular) never reject the call.
{"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","language": "en","translate": true,"mcpConnector": "<your-authorized-slack-connector>","deliveryMode": "summary","mcpTool": "send_message","mcpMessageTemplate": "Transcript of {videoUrl} ({language}, {segmentCount} segments):\n\n{transcript}","mcpArguments": { "channel": "#transcripts", "text": "{message}" }}
Notion, chunked across pages (best for long videos):
{"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","language": "hi","mcpConnector": "<your-authorized-notion-connector>","deliveryMode": "chunked","mcpTool": "notion-create-pages","mcpArguments": {"parent": { "page_id": "<your-page-id>" },"pages": [{"properties": { "title": "Transcript {videoId} (part {part}/{partCount})" },"content": "{transcript}"}]}}
📤 Output Contract & Data Structure
Storage: Apify Dataset — exactly one item per run, with the transcript nested in searchResult.
Pagination: limit & offset on the dataset items endpoint (rarely needed for a single item).
Duplicates: none within a run. Across runs, dedupe on videoId + language.
Every field below is always present on the item — the shape never changes. What varies is the value, and the Toggle / controlled by column names the input that decides it. No output field is toggled on or off by configuration.
| Field | Type | Nullable | Toggle / controlled by | Meaning |
|---|---|---|---|---|
videoUrl | string | No | videoUrl | Normalized https://www.youtube.com/watch?v=<id> URL — not necessarily the URL you passed |
videoId | string | No | videoUrl | Extracted 11-character video ID — verify this matches the video you meant |
requestedLanguage | string | No (may be "") | language | The language code you asked for, trimmed — echoed back verbatim |
language | string | No (may be "") | language + translate | The language actually returned. "" when no transcript was found |
isAutoGenerated | boolean | No | — (source track) | true when the source track is YouTube's ASR (auto-generated) captions |
isTranslated | boolean | No | translate | true when the text is a YouTube auto-translation |
fallbackUsed | boolean | No | language + translate | true when the requested language could not be honoured |
message | string | Yes (null) | language + translate | Plain-English note about any language/translation fallback, or why no transcript exists; null on a clean match |
segmentCount | integer | No | — | Number of segments in searchResult; 0 when status is "no_transcript" |
searchResult | array | No (may be []) | — | Transcript segments, in playback order; [] when status is "no_transcript" |
searchResult[].start | string | No (may be "") | — | Segment start in seconds, as a string — e.g. "3.200" |
searchResult[].dur | string | No (may be "") | — | Segment duration in seconds, as a string — e.g. "2.900" |
searchResult[].text | string | No | — | Segment text, HTML-unescaped, newlines collapsed to spaces. Empty segments are dropped. |
status | string | No | — (video state) | "ok" when a transcript was extracted, "no_transcript" when none could be — branch on this first |
errorCode | string | Yes (null) | — (video state) | null when status is "ok"; otherwise the machine-readable reason (see the matrix below) |
Note for consumers: message is null (not "") when nothing needed explaining, and start/dur are strings for compatibility with the widely-used searchResult transcript format. Cast them before arithmetic, and test message for None/null.
Two item shapes. Every run writes exactly one item, but it comes in two shapes distinguished by status:
status | errorCode | segmentCount | searchResult | message |
|---|---|---|---|---|
"ok" | null | ≥ 1 | populated | null, or a language-fallback note |
"no_transcript" | one of MISSING_VIDEO_URL · BAD_VIDEO_URL · VIDEO_UNAVAILABLE · NO_CAPTIONS · CAPTIONS_BLOCKED · CAPTIONS_UNREACHABLE · EMPTY_TRANSCRIPT · UNEXPECTED_ERROR | 0 | [] | always populated — the human-readable reason |
Example output item
{"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","videoId": "dQw4w9WgXcQ","requestedLanguage": "en","language": "en","isAutoGenerated": false,"isTranslated": false,"fallbackUsed": false,"message": null,"segmentCount": 3,"searchResult": [{ "start": "0.000", "dur": "3.200", "text": "Welcome back to the channel." },{ "start": "3.200", "dur": "2.900", "text": "Today we're talking about web scraping." },{ "start": "6.100", "dur": "4.000", "text": "Let's jump right into the setup." }],"status": "ok","errorCode": null}
Example "no transcript" item
An unavailable, private, deleted, or caption-less video produces a successful run with this shape instead:
{"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","videoId": "dQw4w9WgXcQ","requestedLanguage": "en","language": "","isAutoGenerated": false,"isTranslated": false,"fallbackUsed": false,"message": "This video is not available on YouTube (YouTube says: \"Video unavailable\"). It may be private, deleted, region-blocked, or the ID may be wrong, so there is no transcript to extract.","segmentCount": 0,"searchResult": [],"status": "no_transcript","errorCode": "VIDEO_UNAVAILABLE"}
Fallback example — Hindi requested, only English captions exist, translate: false:
{"requestedLanguage": "hi","language": "en","isTranslated": false,"fallbackUsed": true,"message": "The requested language 'hi' is not available and translation is turned off. Returning the available caption language 'en'. Available languages: en, es."}
What a successful run looks like
{"status": "SUCCEEDED","defaultDatasetId": "<datasetId>","stats": { "itemCount": 1 }}
itemCount: 1 is the complete result — but it does not by itself mean a transcript was extracted. Read the item's status:
status: "ok"→ transcript present;segmentCountgives its length.status: "no_transcript"→ the run worked, the video didn't yield captions. ReaderrorCodeandmessagefor the reason, and the run's terminal status message showsNo transcript (<CODE>): <message>.
Nothing about the input or the video fails the run any more. A missing or unparseable videoUrl, and every fact about the video's own state, is reported as a SUCCEEDED run carrying an errorCode and a plain-English message. A FAILED run now indicates a platform-level problem — an aborted run, a timeout, or an exhausted spend cap — not something your input said.
▶️ Invocation & Integration
Lifecycle: Discover → Understand → Configure → Execute → Retrieve → Validate. Authenticate every call with Authorization: Bearer <APIFY_TOKEN>.
Apify Console
Open the Actor → Input tab → paste the YouTube video URL → pick a Preferred language → leave Translate on → (optional) choose an MCP connector, tool, and template → Start → read the Dataset tab (JSON, CSV, Excel, XML).
Model Context Protocol (MCP)
Add this Actor directly as an AI tool in Claude Desktop, Cursor, VS Code, or LangChain agents:
claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/youtube-transcript-scraper"
Python (apify-client)
import osfrom apify_client import ApifyClientclient = ApifyClient(os.getenv("APIFY_TOKEN"))run = client.actor("techforce.global/youtube-transcript-scraper").call(run_input={"videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","language": "en","translate": True,"deliveryMode": "none",})item = client.dataset(run["defaultDatasetId"]).list_items().items[0]# Always branch on status: a SUCCEEDED run can legitimately carry no transcript.if item["status"] != "ok":raise SystemExit(f'No transcript ({item["errorCode"]}): {item["message"]}')print(item["language"], item["segmentCount"], "segments")if item["fallbackUsed"]:print("Language fallback:", item["message"])full_text = " ".join(seg["text"] for seg in item["searchResult"])print(full_text[:400])
Batch several videos by looping the Actor (one video per run):
urls = ["https://youtu.be/aaaaaaaaaaa", "https://youtu.be/bbbbbbbbbbb"]for url in urls:run = client.actor("techforce.global/youtube-transcript-scraper").call(run_input={"videoUrl": url, "language": "en", "deliveryMode": "none"})print(url, run["status"])
JavaScript / TypeScript (apify-client)
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('techforce.global/youtube-transcript-scraper').call({videoUrl: 'https://youtu.be/dQw4w9WgXcQ',language: 'hi',translate: true,deliveryMode: 'none',});const { items } = await client.dataset(run.defaultDatasetId).listItems();const [transcript] = items;if (transcript.status !== 'ok') {// Unavailable video, no captions, bot wall — the run succeeded, the video didn't.throw new Error(`No transcript (${transcript.errorCode}): ${transcript.message}`);}console.log(`${transcript.language}: ${transcript.segmentCount} segments`);console.log(transcript.searchResult.map((s) => s.text).join(' ').slice(0, 400));
cURL — synchronous (returns the item directly; 300s limit)
curl -X POST \-H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"videoUrl":"https://www.youtube.com/watch?v=dQw4w9WgXcQ","language":"en","deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~youtube-transcript-scraper/run-sync-get-dataset-items'
cURL — asynchronous with a spend cap (recommended for production)
# 1. Start execution with a hard spend limitcurl -X POST -H 'Content-Type: application/json' \-H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \-d '{"videoUrl":"https://youtu.be/dQw4w9WgXcQ","language":"en","deliveryMode":"none"}' \'https://api.apify.com/v2/acts/techforce.global~youtube-transcript-scraper/runs?maxTotalChargeUsd=1'# 2. Pollcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/actor-runs/<runId>'# 3. Retrieve when SUCCEEDEDcurl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \'https://api.apify.com/v2/datasets/<DATASET_ID>/items?clean=true&format=json'
⚠️ Autonomous Agent Error Handling & Resolution Matrix
Read this first: every problem with the video is reported as a SUCCEEDED run whose single dataset item carries status: "no_transcript" and an errorCode. Caller mistakes are reported identically: a missing videoUrl as MISSING_VIDEO_URL, an unusable one as BAD_VIDEO_URL. No input error fails the run. An agent that checks run status alone will mistake "this video has no captions" for success with data; check item["status"]. Connector delivery is skipped entirely when there is no transcript, so a no_transcript outcome never posts an empty message to Slack or Notion.
| Error Code | Detection Condition | Underlying Cause | Deterministic Agent Action |
|---|---|---|---|
AUTH_INVALID | HTTP 401 | Missing or malformed APIFY_TOKEN | Abort & Prompt User: request a valid API token. |
RATE_LIMITED | HTTP 429 | Apify API rate / concurrency limits | Retry: exponential backoff (2s, 4s, 8s). |
SYNC_TIMEOUT | HTTP 408 on the sync endpoint | Run exceeded the 300s synchronous ceiling | Reconfigure: switch to async POST /runs + polling. |
MISSING_VIDEO_URL | SUCCEEDED, item errorCode: "MISSING_VIDEO_URL", status: "no_transcript" | videoUrl omitted or blank (no default exists) | Modify Input: pass videoUrl explicitly. The message field says what shapes are accepted. |
BAD_VIDEO_URL | SUCCEEDED, item errorCode: "BAD_VIDEO_URL", status: "no_transcript"; the offending value is echoed in videoUrl | Not a YouTube URL / no 11-char ID present | Modify Input: pass a clean watch URL, youtu.be link, or bare ID. |
WRONG_VIDEO | SUCCEEDED but videoId ≠ intended video | Last-resort 11-char token match latched onto the wrong substring | Validate & Re-run: always compare returned videoId against your input. |
VIDEO_UNAVAILABLE | SUCCEEDED, item errorCode: "VIDEO_UNAVAILABLE" | Video is private, deleted, region-blocked, or the ID is wrong — both fetch paths report it unavailable | Do not retry. Verify the video opens in a browser; ask the user for a valid URL. |
NO_CAPTIONS | SUCCEEDED, item errorCode: "NO_CAPTIONS" | Video exists but has no caption tracks at all | Do not retry. Route to an ASR/Whisper service; this Actor cannot transcribe audio. |
CAPTIONS_BLOCKED | SUCCEEDED, item errorCode: "CAPTIONS_BLOCKED" | YouTube challenged the request (bot wall, sign-in, age gate) | Reconfigure & retry once: enable the RESIDENTIAL proxy group. |
CAPTIONS_UNREACHABLE | SUCCEEDED, item errorCode: "CAPTIONS_UNREACHABLE" | Neither resolution path returned usable caption metadata | Retry once with a residential proxy; if it repeats, treat as unavailable. |
EMPTY_TRANSCRIPT | SUCCEEDED, item errorCode: "EMPTY_TRANSCRIPT" | Caption track resolved but parsed to zero usable text | Retry: re-run with translate: true or a different language. |
UNEXPECTED_ERROR | SUCCEEDED, item errorCode: "UNEXPECTED_ERROR" | An unhandled upstream shape or network fault; the log carries the stack trace | Retry once, then report the run ID. |
PROXY_UNAVAILABLE | Log Could not set up proxy configuration or Running without a proxy | Residential groups require a paid Apify plan | Prompt User: upgrade the plan; expect bot-wall failures until then. |
LANGUAGE_FALLBACK | SUCCEEDED with fallbackUsed: true | Requested language unavailable | Not an error. Read language and message; re-run with translate: true if a translation is acceptable. |
TRANSLATION_FALLBACK | SUCCEEDED with isTranslated: false after requesting a foreign language | Translation attempt returned no text | Not an error. Consume the returned language or translate downstream. |
DELIVERY_SKIPPED | Log no tool name was provided | mcpConnector set but mcpTool empty | Modify Input: set mcpTool; the dataset item is already saved. |
DELIVERY_TOOL_UNKNOWN | Log Tool 'x' is not available on this connector | Wrong tool name for that connector | Modify Input: pick a name from the Available tools: list in the same log line. |
DELIVERY_EMPTY | Connector called, body empty | mcpArguments omitted the {message} placeholder | Modify Input: map {message} inside mcpArguments. |
MCP_PROXY_MISSING | Log APIFY_MCP_PROXY_URL is not set | Running locally instead of on the platform | Reconfigure: apify push and run on the platform. |
CHARGE_LIMIT | Run aborted immediately, before any work | maxTotalChargeUsd set below what the run costs to start | Prompt User: raise the cap. Check the Store page for the current rate. |
Provenance of this table: every
errorCode,statusvalue and log-line condition above is taken from this Actor's own source and verified against it. The four HTTP rows (401,408,429) describe the Apify API transport layer, not this Actor — confirm them against the Apify API reference before wiring them into automated retry logic.
🗣️ Natural Language → Actor Mapping
| User says | Intent | Constructed Actor input |
|---|---|---|
| "Get me the transcript of this YouTube video" | Transcript extraction | {"videoUrl":"<url>","language":"en"} |
| "What is said in this video?" | Content Q&A / retrieval context | {"videoUrl":"<url>"} then read searchResult[].text |
| "Summarize this YouTube video" | Summarization input | {"videoUrl":"<url>"} → summarize searchResult text yourself (the Actor returns raw captions) |
| "Give me the Hindi transcript of this talk" | Localized transcript | {"videoUrl":"<url>","language":"hi","translate":true} |
| "Transcript in Spanish, but only if it's a real Spanish caption" | Strict language | {"videoUrl":"<url>","language":"es","translate":false} then check fallbackUsed |
| "Pull the captions of this Short" | Shorts extraction | {"videoUrl":"https://www.youtube.com/shorts/<id>"} |
| "I need timestamps so I can cut clips" | Subtitle / clipping workflow | {"videoUrl":"<url>"} then read start + dur per segment |
| "Turn this video into SRT subtitles" | Localization | {"videoUrl":"<url>","language":"en"} then format segments as SRT downstream |
| "Save this video's transcript to Notion" | Pipeline/delivery | {"videoUrl":"<url>","mcpConnector":"notion","deliveryMode":"chunked","mcpTool":"notion-create-pages"} |
| "Post the transcript to Slack #content" | Pipeline/delivery | {"videoUrl":"<url>","mcpConnector":"slack","deliveryMode":"summary","mcpTool":"send_message","mcpArguments":{"channel":"#content","text":"{message}"}} |
| "Open a Jira ticket with this transcript attached" | Pipeline/delivery | {"videoUrl":"<url>","mcpConnector":"jira","deliveryMode":"summary","mcpTool":"create_issue"} |
| "Get transcripts for these 12 videos" | Batch extraction | 12 sequential runs, one videoUrl each — then merge on videoId |
| "Get transcripts for every video on this channel" | Channel-wide extraction | ➡️ Route to YouTube Full Channel Transcripts Scraper |
| "How many views and comments does this video have?" | Metadata | ➡️ Route to YouTube Scraper |
| "List all the Shorts on this channel" | Shorts catalogue | ➡️ Route to YouTube Shorts Scraper |
| "Transcribe this video — it has no captions" | ASR request | ❌ Out of scope — route to a speech-recognition service |
Should NOT route here: videos with no caption track (needs ASR) · playlists, channels, or batch lists in a single run (→ Full Channel Transcripts) · comments, views, likes, or channel statistics (→ YouTube Scraper) · a channel's Shorts or video-file downloads (→ Shorts Scraper) · private, unlisted, or age-gated videos · LLM summarization or translation of the text · non-YouTube video platforms (Vimeo, TikTok, Twitch).
🧭 Agent Execution & Routing Logic
[Input User Query]│▼1. Target is a YouTube video (youtube.com / youtu.be)? NO → Route to the correct platform Actor│ YES▼2. Wants transcript/caption text (not audio, stats, comments)? NO → Route elsewhere / abort│ YES▼3. Is the video public (not private/unlisted/age-gated)? NO → Reject│ YES▼4. Does the video have captions? UNKNOWN → Try; if errorCode == NO_CAPTIONS route to an ASR service│▼5. Can a video ID be extracted from the input? NO → Prompt user for a clean URL or 11-char ID│ YES▼6. Is the requested language in the 13-value enum? NO → Use "en" and translate downstream│ YES▼7. Is a non-requested language acceptable? NO → set translate:false and check fallbackUsed│ YES → keep translate:true (default)▼8. More than one video needed? YES → Whole channel? Route to Full Channel Transcripts Scraper│ NO A few videos? Loop: one run per video▼9. Deliver to a connector? YES → set mcpConnector + mcpTool + {message} in mcpArguments│ NO → set deliveryMode "none"▼[Execute Apify Actor]│├──► run "SUCCEEDED" ──► item.status == "ok"?│ ├── YES → verify videoId, read language / isTranslated / fallbackUsed / message → consume searchResult│ └── NO → read item.errorCode → Error Handling Matrix (retry, re-route to ASR, or ask the user)└──► run "FAILED" ──► platform-level only (abort, timeout, spend cap) → not an input problem
💰 Cost Control & Pricing Transparency
Billing model: usage-based. You pay for the Apify platform usage a run consumes. Current rates are on the Actor's Apify Store page, and the Console shows a live total while a run is in progress.
- One run = one video, any length. There is no per-segment or per-character charge, so a 3-minute clip and a 3-hour lecture are the same shape of run.
- Transcript retrieval is short and bounded. The expensive variable is not video length — it is whether the request is challenged and has to fall back.
- Autonomous spend caps: pass
maxTotalChargeUsdon the run endpoint for a hard per-execution ceiling. It is the only hard stop; input validation bounds the work, not the spend.
Worked examples — relative run shape
Multiply by your account's usage rate from the Store page to get a currency figure.
| Scenario | Runs | Relative cost per run | Notes |
|---|---|---|---|
| One video, captions present | 1 | Baseline | The normal case. |
| One video, 3-hour lecture | 1 | ≈ Baseline | Length barely matters — the caption track is one fetch. |
| One video, primary path challenged | 1 | Slightly above baseline | Fallback path adds one request. |
| One video, no captions at all | 1 | Below baseline | Ends early with NO_CAPTIONS; no parsing work. |
| Same video in 3 languages | 3 | 3 × baseline | Each language is a separate run. |
| 50 unrelated videos | 50 | 50 × baseline | Loop from your orchestrator. |
| A whole channel | 1 | — | ➡️ Use Full Channel Transcripts Scraper instead of 200 runs here. |
Cost-control tips: validate the URL before spending a run; check the video's transcript panel
in a browser first if you are unsure captions exist; cache results by videoId + language so a
repeat request costs nothing; and remember each language is a separate run — so for multi-language
work, decide the set up front.
🛠️ Troubleshooting
Most failures are caption availability or proxy issues, not scraper bugs. Work through this table top to bottom — the first three rows account for the large majority of support tickets.
| # | Symptom you see | Most likely cause | Fix |
|---|---|---|---|
| 1 | SUCCEEDED but errorCode: "VIDEO_UNAVAILABLE" — This video is not available on YouTube… | The video is private, deleted, region-blocked, or the ID is wrong. Both fetch paths agreed | Open the URL in a browser. If it doesn't play for you either, no scraper can reach it — retrying and changing proxy will not help. |
| 2 | SUCCEEDED but errorCode: "CAPTIONS_BLOCKED" / "CAPTIONS_UNREACHABLE" | YouTube challenged the request, or returned no caption metadata on either path | Enable RESIDENTIAL Apify Proxy (the default) and retry once. Datacenter/local IPs are blocked routinely. |
| 3 | SUCCEEDED but errorCode: "NO_CAPTIONS" | The video genuinely has no caption track | Expected. This Actor reads captions; it does not transcribe audio. Use an ASR/Whisper service. |
| 3a | SUCCEEDED but errorCode: "EMPTY_TRANSCRIPT" | Caption track resolved but every segment parsed empty | Re-run with translate: true, or request a different language. |
| 3b | Your pipeline treats a no_transcript run as a transcript | You checked run status instead of item["status"] | Branch on item["status"] == "ok" before reading searchResult. |
| 4 | Wrong language in the output | Requested language had no native track; you got a translation or a fallback | Read language, isTranslated, fallbackUsed, message. Set translate: false if only a native track will do. |
| 5 | translate: false but the run still succeeded in another language | By design — it disables translation, it does not fail the run | Branch on fallbackUsed: true, not on run status. |
| 6 | en-US / pt-BR rejected or ignored | Only the 13 base enum codes are valid | Pass en, pt, … Region variants are not selectable. |
| 7 | Text reads like speech-to-text with no punctuation | The source track is YouTube's auto-generated ASR caption | Check isAutoGenerated: true. Nothing to fix — that is the only track available. |
| 8 | videoId is not the video you meant | A last-resort regex matched an 11-char token in a malformed input | Always pass a clean URL or bare ID, and assert the returned videoId. |
| 9 | Only one dataset item | By design — one run = one video = one item, transcript nested in searchResult | Read segmentCount for length; unwind searchResult for a per-segment table. |
| 10 | start / dur arithmetic fails | They are strings ("3.200"), not numbers | Cast to float before arithmetic. |
| 11 | message is null in your consumer | null means "nothing needed explaining" | Test for None/null, not for "". |
| 12 | Log: Running without a proxy | No proxy configured or unavailable on the plan | Enable Apify Proxy residential group; a paid plan is required. |
| 13 | Log shows the primary resolution path failing, but the run succeeded | Normal — the fallback path took over | No action needed. |
| 14 | Need many videos and each run is a separate charge | One video per run by design | For a whole channel use Full Channel Transcripts Scraper; otherwise loop and cache by videoId + language. |
| 15 | Slack / Notion / Jira received nothing | Delivery needs both mcpConnector and mcpTool; otherwise it is skipped with a warning | Set both. The dataset item is still saved — your data is never lost. |
| 16 | Log: APIFY_MCP_PROXY_URL is not set | You ran locally with apify run; connectors only resolve on the platform | Deploy with apify push and run on the platform. |
| 17 | Connector fired, but the message body is empty | mcpArguments did not reference {message} | Map {message} in mcpArguments and populate mcpMessageTemplate. |
| 18 | Notion rejects or truncates a long transcript | Single-call payload exceeded block/timeout caps | Use deliveryMode: "chunked" and put {part}/{partCount} in the page title. |
| 19 | Every Notion page looks identical | {part}/{partCount} missing from the title template | Add them: "Transcript {videoId} (part {part}/{partCount})". |
| 20 | Hundreds of connector calls fired | deliveryMode: "perSegment" sends one call per segment | Switch to chunked or summary unless per-line records are the goal. |
| 21 | Delivery fired when you only wanted a dataset | deliveryMode defaults to summary, not none | Set deliveryMode: "none" or leave mcpConnector empty. |
| 22 | HTTP 401 / 403 | Missing, expired, or malformed APIFY_TOKEN | Regenerate in Apify Console → Settings → API & Integrations, send as Authorization: Bearer <token>. |
| 23 | HTTP 429 | Apify account concurrency / rate limits — not YouTube blocking | Retry with exponential backoff (2s → 4s → 8s). |
| 24 | HTTP 408 on run-sync-get-dataset-items | The synchronous endpoint has a hard 300-second ceiling | Switch to async: POST /runs → poll /actor-runs/<runId> → fetch dataset. |
Diagnostic checklist before opening an issue
- Open the video in a browser and check the transcript panel (⋯ → Show transcript). If YouTube shows nothing there, the video has no captions and no scraper can produce them.
- Confirm the proxy. Residential group on, paid plan active. This is the single most common cause of caption-retrieval failures.
- Baseline run.
videoUrlonly,language: "en",translate: true,deliveryMode: "none". - Check the resolved
videoIdin the output or the run log against the video you meant. - Read the run log — it names which resolution path succeeded and which caption language was selected.
If the issue survives all five steps, open an Issues ticket on the Actor page (or email support) with the run ID, the video URL, and what you expected. Run IDs let us replay the exact execution.
❓ FAQ
Setup & access
Do I need a YouTube or Google account, API key, or OAuth app?
No. The Actor reads publicly available caption tracks. You need an Apify account and API token; you need no Google credentials whatsoever.
Is this the official YouTube API?
No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google.
Why can't I just use the official YouTube Data API?
captions.download only works for videos you own, via OAuth. For any third-party video it is not an option — which is the main reason this Actor exists.
Do I need to configure proxies?
The residential Apify Proxy is on by default and you should leave it on — YouTube serves datacenter IPs a bot-check wall. Residential groups require a paid Apify plan.
Can I plug it into Claude, Cursor, or a LangChain agent?
Yes — it is a native MCP tool:
claude mcp add --transport http apify "https://mcp.apify.com?tools=techforce.global/youtube-transcript-scraper"
Input & languages
Can I pass a playlist, channel, or list of videos?
No — one video per run. For a whole channel, use YouTube Full Channel Transcripts Scraper, which takes a channel URL and processes up to 50 videos concurrently. For a few specific unrelated videos, loop this Actor from your orchestration layer (each video is a separate run, and a separate charge).
Which languages are supported?
13: en, hi, gu, pt, es, fr, de, it, ja, ko, zh, ru, ar. Region variants like en-US are not valid inputs.
What happens if my language isn't available?
With translate: true (default) you get a YouTube auto-translation. With translate: false you get the video's own caption language, fallbackUsed: true, and a message listing what was available. The run does not fail for language reasons.
Does it work on Shorts?
Yes — Shorts, embed, /live/, youtu.be, mobile and youtube-nocookie hosts, and bare 11-character IDs all resolve.
Output & data
Why is the whole transcript in one dataset item?
One run handles one video, so the transcript is a nested searchResult array on a single item. Use the Console's Transcript view (or unwind=searchResult on the API) for a per-segment table.
Why are start and dur strings?
For compatibility with the widely-used searchResult transcript format. Cast them to float before doing arithmetic.
Does it summarize or translate with an LLM?
No. It returns YouTube's caption text — raw, timestamped, and unmodified apart from HTML unescaping. Summarize downstream.
Can it transcribe a video that has no captions?
No. There is no speech recognition in this Actor. Use an ASR service for caption-less videos. The run itself still succeeds and tells you so, with status: "no_transcript" and errorCode: "NO_CAPTIONS".
Why did my run succeed but return no transcript?
Because the video, not the Actor, was the problem: it may be private, deleted, region-blocked, caption-less, or YouTube may have challenged the request. Rather than crashing, the Actor writes one dataset item with status: "no_transcript", a machine-readable errorCode, and a plain-English message — so scheduled runs and agent pipelines get a readable reason instead of an alert storm from failed runs. Read item["status"] first, always.
Which outcomes still fail the run?
None of them. A missing videoUrl (MISSING_VIDEO_URL), an unusable one (BAD_VIDEO_URL) and every fact about the video's own state all end as a SUCCEEDED run whose single item carries an errorCode and a validation message — so API and MCP callers get a readable reason instead of a stack trace. A FAILED run now means a platform-level problem: an abort, a timeout, or an exhausted spend cap.
Delivery
What is the MCP connector for?
It pushes the transcript straight into a tool you have already authorized on your Apify account — Notion, Slack, Jira, GitHub, Linear, Airtable, or any MCP-compatible connector — with no glue code. Credentials stay private: delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens.
Which delivery mode should I use?
summary for short videos, chunked for long transcripts into services with request caps (Notion), perSegment for per-line records (logs, row-per-segment sheets), none for dataset-only runs.
Does delivery work when running locally?
No. Connectors resolve through the Apify MCP Proxy, which only exists on the platform. Locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset item is saved as normal.
Does it deliver anything when there is no transcript?
No. A no_transcript outcome skips delivery entirely, so your Slack channel or Notion database never receives an empty transcript. The reason is in the dataset item and the run's status message.
Limits & pricing
What does it cost?
Usage-based: you pay for the platform usage a run consumes. One video per run, any length, with no per-segment or per-character fee. Current rates are on the Actor's Apify Store page, and the Console shows a live total while a run is in progress.
How do I guarantee an agent never overspends?
Pass maxTotalChargeUsd on the run endpoint — it is a hard per-run ceiling — and cache transcripts
by videoId + language so repeats cost nothing.
🔍 Companion machine-readable files
| File | Purpose |
|---|---|
.actor/actor.json | Identity, version, storage and dockerfile wiring |
.actor/input_schema.json | Authoritative typed input contract — videoUrl, language enum, translate, proxy editor, deliveryMode enum |
.actor/dataset_schema.json | Output field metadata and both Console views (Overview + unwound Transcript) — consumed by the Apify MCP server to describe results to agents |
.actor/output_schema.json | Declares where results are stored |
my_actor/main.py | Video-ID extraction, caption resolution and parsing, language fallback, MCP delivery |
my_actor/connector.py | MCP connector session handling and {placeholder} rendering |
If any table on this page disagrees with the schema files, the schema files win.
🔗 Related Actors
Other YouTube Actors
This Actor is the single-video specialist: one URL in, one timestamped transcript out. Use these siblings when the job is bigger or different.
| Actor | Best for | Why pick it over this one |
|---|---|---|
| YouTube Full Channel Transcripts Scraper | Transcripts for every video in a channel — accepts @handle, /channel/ID, /c/name, /user/name, processes up to 50 videos concurrently, and returns video + channel metadata alongside the transcript | You want a whole channel (or its latest N videos) in one run instead of orchestrating one run per video. |
| YouTube Scraper: Videos, Channels & Comments | Video, channel, and comment metadata from search queries, channel URLs, video URLs, or hashtags — up to 500 videos per input and 1,000 comments per video | You need views, likes, descriptions, channel stats, or comment threads — none of which this transcript Actor returns. |
| YouTube Shorts Scraper | Every Short from a channel with engagement metrics (views, likes, comments, hashtags) and optional video downloads | You're working through a channel's Shorts catalogue, or you need the video files themselves. |
Pick by intent: one video's words → this Actor · a channel's words → Full Channel Transcripts · numbers, comments, and search discovery → YouTube Scraper · a channel's Shorts and their files → Shorts Scraper.
Enrichment & downstream pipeline
| Actor | Use it for |
|---|---|
| Advanced Website Crawling Actor | Crawl the creator's own site or show notes for clean HTML/Markdown/text context alongside the transcript |
| Google Maps Scraper – Business Data | Enrich businesses or venues mentioned in a transcript into full business records |
| GOV.UK Business Scraper | Match UK company names heard in a video against Companies House records |
Suggested pipeline patterns
✍️ Content repurposing
YouTube Transcript Scraper → transcript text into your LLM of choice → blog draft, thread, newsletter → publish via a Notion/Slack connector.
🌍 Localization
One run per target language (hi, gu, es, …) → format start/dur/text as SRT downstream → upload as subtitle tracks.
🔎 Video knowledge base
YouTube Full Channel Transcripts Scraper for the channel backfill → this Actor for each new upload → store by videoId + language → index searchResult text for semantic search over everything you've published.
📊 Transcript + engagement analysis YouTube Scraper for views, likes, and comments → this Actor for the words of the videos that over-performed → correlate topics against engagement.
🤖 Agent retrieval tool Register the Actor as an MCP tool so an agent can pull a video's transcript on demand and answer questions with timestamps.
Browse all Actors by Techforce Global at scraper.techforce.global.
🔐 Compliance & Data Privacy
Trademark & affiliation. This is an independent Actor. It is not affiliated with, endorsed by, or sponsored by YouTube or Google. YouTube™ is a trademark of Google LLC; all trademarks are the property of their respective owners.
Public data only — and no credentials, ever. This Actor reads publicly available caption tracks. It does not log into any account, does not accept or store any Google/YouTube credential, and does not bypass authentication. Private, unlisted and age-gated videos are inaccessible to it by design, and a request for one returns status: "no_transcript" rather than a partial result.
GDPR / CCPA — what you are taking on. The Actor itself collects no personal data about you and requires no account linkage. But a transcript is speech, and speech is about people:
- The speaker is identifiable. A transcript of an interview, vlog or conference talk is that person's words. Under GDPR / UK GDPR and CCPA/CPRA, storing it can constitute processing personal data about them — and about any third party named aloud in the recording.
- Special-category content cannot be filtered in advance. Health, politics, religion and sexuality come up in spoken content, and you cannot know which transcripts contain it until after retrieval. Keep retention short and access controlled.
- Auto-generated transcripts are not verbatim quotes. When
isAutoGenerated: true, a machine wrote the text; mis-transcribed names and inverted negations are common. Attributing it to a speaker as their exact words is a misattribution — and potentially a defamation — risk. Check the flag before quoting. - Minimise. If your question is about topics rather than people, you need the text, not the identity of everyone in it. Aggregate and discard early.
Copyright. Transcripts are the creator's copyrighted expression. Internal research, search indexing and accessibility use sit on far safer ground than republishing transcripts as your own content, or using them as a substitute for watching the video. Using them to train a model is a legally distinct act from analysing them — take advice rather than relying on "it was public".
Terms of Service. YouTube's terms restrict automated access. This Actor does not remove that risk; running it means accepting it. The official Data API is the sanctioned route — but its captions.download endpoint only serves videos you own, which is the gap this Actor exists to fill.
🆘 Support & Custom Pipeline Engineering
Whole-channel coverage is already a product — see YouTube Full Channel Transcripts Scraper. Need something beyond it: scheduled multi-channel monitoring, playlist coverage, or a full content pipeline into your CMS?
- Email: bhavin.shah@techforceglobal.com
- Custom Enterprise Integrations: Book a 15-Minute Technical Consultation
- Maintained by: Techforce Global — Specialists in High-Performance Web Scrapers and Agentic Workflows.
Made with ❤️ by Techforce Global Specialists in High-Performance Video & Transcript Data Extraction and AI Automation.
🏷️ Structured data for search & AI discovery
Two blocks: SoftwareApplication describing the Actor, and FAQPage wrapping the FAQ above so those answers are eligible for rich results. The FAQPage entries are generated from the visible FAQ and match it question-for-question.
{"@context": "https://schema.org","@type": "SoftwareApplication","name": "YouTube Transcript Scraper - Captions to Notion, Slack & Jira","alternateName": "YouTube Captions API Alternative","applicationCategory": "DeveloperApplication","operatingSystem": "Cloud (Apify platform)","description": "The YouTube captions API alternative for videos you don't own: extracts the full timestamped transcript of any public YouTube video as structured JSON — every segment with start time, duration and text — in 13 languages with automatic translation fallback, without a Google API key, OAuth, or video ownership. Public caption tracks only: no account login, no stored credentials, and no access to private, unlisted or age-gated videos, with GDPR/CCPA and speaker-identifiability guidance included.","url": "https://apify.com/techforce.global/youtube-transcript-scraper","offers": {"@type": "Offer","priceCurrency": "USD","description": "Usage-based: billed for the Apify platform usage a run consumes. One video per run, any length, with no per-segment charge. Current rates are shown on the Actor's Apify Store page."}}
{"@context": "https://schema.org","@type": "FAQPage","mainEntity": [{"@type": "Question","name": "Do I need a YouTube or Google account, API key, or OAuth app?","acceptedAnswer": {"@type": "Answer","text": "No. The Actor reads publicly available caption tracks. You need an Apify account and API token; you need no Google credentials whatsoever."}},{"@type": "Question","name": "Is this the official YouTube API?","acceptedAnswer": {"@type": "Answer","text": "No. This is an independent Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google."}},{"@type": "Question","name": "Why can't I just use the official YouTube Data API?","acceptedAnswer": {"@type": "Answer","text": "captions.download only works for videos you own, via OAuth. For any third-party video it is not an option — which is the main reason this Actor exists."}},{"@type": "Question","name": "Do I need to configure proxies?","acceptedAnswer": {"@type": "Answer","text": "The residential Apify Proxy is on by default and you should leave it on — YouTube serves datacenter IPs a bot-check wall. Residential groups require a paid Apify plan."}},{"@type": "Question","name": "Can I plug it into Claude, Cursor, or a LangChain agent?","acceptedAnswer": {"@type": "Answer","text": "Yes — it is a native MCP tool: claude mcp add --transport http apify \"https://mcp.apify.com?tools=techforce.global/youtube-transcript-scraper\""}},{"@type": "Question","name": "Can I pass a playlist, channel, or list of videos?","acceptedAnswer": {"@type": "Answer","text": "No — one video per run. For a whole channel, use YouTube Full Channel Transcripts Scraper, which takes a channel URL and processes up to 50 videos concurrently. For a few specific unrelated videos, loop this Actor from your orchestration layer (each video is a separate run, and a separate charge)."}},{"@type": "Question","name": "Which languages are supported?","acceptedAnswer": {"@type": "Answer","text": "13: en, hi, gu, pt, es, fr, de, it, ja, ko, zh, ru, ar. Region variants like en-US are not valid inputs."}},{"@type": "Question","name": "What happens if my language isn't available?","acceptedAnswer": {"@type": "Answer","text": "With translate: true (default) you get a YouTube auto-translation. With translate: false you get the video's own caption language, fallbackUsed: true, and a message listing what was available. The run does not fail for language reasons."}},{"@type": "Question","name": "Does it work on Shorts?","acceptedAnswer": {"@type": "Answer","text": "Yes — Shorts, embed, /live/, youtu.be, mobile and youtube-nocookie hosts, and bare 11-character IDs all resolve."}},{"@type": "Question","name": "Why is the whole transcript in one dataset item?","acceptedAnswer": {"@type": "Answer","text": "One run handles one video, so the transcript is a nested searchResult array on a single item. Use the Console's Transcript view (or unwind=searchResult on the API) for a per-segment table."}},{"@type": "Question","name": "Why are start and dur strings?","acceptedAnswer": {"@type": "Answer","text": "For compatibility with the widely-used searchResult transcript format. Cast them to float before doing arithmetic."}},{"@type": "Question","name": "Does it summarize or translate with an LLM?","acceptedAnswer": {"@type": "Answer","text": "No. It returns YouTube's caption text — raw, timestamped, and unmodified apart from HTML unescaping. Summarize downstream."}},{"@type": "Question","name": "Can it transcribe a video that has no captions?","acceptedAnswer": {"@type": "Answer","text": "No. There is no speech recognition in this Actor. Use an ASR service for caption-less videos. The run itself still succeeds and tells you so, with status: \"no_transcript\" and errorCode: \"NO_CAPTIONS\"."}},{"@type": "Question","name": "Why did my run succeed but return no transcript?","acceptedAnswer": {"@type": "Answer","text": "Because the video, not the Actor, was the problem: it may be private, deleted, region-blocked, caption-less, or YouTube may have challenged the request. Rather than crashing, the Actor writes one dataset item with status: \"no_transcript\", a machine-readable errorCode, and a plain-English message — so scheduled runs and agent pipelines get a readable reason instead of an alert storm from failed runs. Read item[\"status\"] first, always."}},{"@type": "Question","name": "Which outcomes still fail the run?","acceptedAnswer": {"@type": "Answer","text": "None of them. A missing videoUrl (MISSING_VIDEO_URL), an unusable one (BAD_VIDEO_URL) and every fact about the video's own state all end as a SUCCEEDED run whose single item carries an errorCode and a validation message — so API and MCP callers get a readable reason instead of a stack trace. A FAILED run now means a platform-level problem: an abort, a timeout, or an exhausted spend cap."}},{"@type": "Question","name": "What is the MCP connector for?","acceptedAnswer": {"@type": "Answer","text": "It pushes the transcript straight into a tool you have already authorized on your Apify account — Notion, Slack, Jira, GitHub, Linear, Airtable, or any MCP-compatible connector — with no glue code. Credentials stay private: delivery runs through the Apify MCP Proxy, so the Actor never sees your connector tokens."}},{"@type": "Question","name": "Which delivery mode should I use?","acceptedAnswer": {"@type": "Answer","text": "summary for short videos, chunked for long transcripts into services with request caps (Notion), perSegment for per-line records (logs, row-per-segment sheets), none for dataset-only runs."}},{"@type": "Question","name": "Does delivery work when running locally?","acceptedAnswer": {"@type": "Answer","text": "No. Connectors resolve through the Apify MCP Proxy, which only exists on the platform. Locally you'll see APIFY_MCP_PROXY_URL is not set and the dataset item is saved as normal."}},{"@type": "Question","name": "Does it deliver anything when there is no transcript?","acceptedAnswer": {"@type": "Answer","text": "No. A no_transcript outcome skips delivery entirely, so your Slack channel or Notion database never receives an empty transcript. The reason is in the dataset item and the run's status message."}},{"@type": "Question","name": "What does it cost?","acceptedAnswer": {"@type": "Answer","text": "Usage-based: you pay for the platform usage a run consumes. One video per run, any length, with no per-segment or per-character fee. Current rates are on the Actor's Apify Store page, and the Console shows a live total while a run is in progress."}},{"@type": "Question","name": "How do I guarantee an agent never overspends?","acceptedAnswer": {"@type": "Answer","text": "Pass maxTotalChargeUsd on the run endpoint — it is a hard per-run ceiling — and cache transcripts by videoId + language so repeats cost nothing."}}]}