YouTube Transcript Scraper - Captions to Notion, Slack & Jira. avatar

YouTube Transcript Scraper - Captions to Notion, Slack & Jira.

Pricing

from $0.10 / actor start

Go to Apify Store
YouTube Transcript Scraper - Captions to Notion, Slack & Jira.

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

Techforce Global

Maintained by Community

Actor stats

1

Bookmarked

1

Total users

1

Monthly active users

10 days ago

Last modified

Share

YouTube Transcript Scraper - Captions to Notion, Slack & Jira

Apify Actor MCP Ready Pricing Public data only Target

The YouTube captions API alternative for videos you don't own. Google's captions.download endpoint 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

CredentialsNone 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 scopePublic 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 / CCPAA 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 safetyisAutoGenerated: 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 / CapabilityOfficial YouTube Data API v3YouTube Transcript Scraper Actor (Apify)
Download captions of videos you don't owncaptions.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 blockingN/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 modelFree within quota, then quota-increase requestsUsage-based — no quota wall to hit

Quota costs and the captions.download ownership 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: false
values: ["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: 1
dataset_items_per_run: 1
languages_supported: 13
requires_existing_captions: true
pricing:
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 video
has no captions at all (this Actor does not run its own speech
recognition); 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.be short 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 with fallbackUsed: true and a plain-English message explaining 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-readable errorCode, and a plain-English message — so schedules don't alert and agents get a reason they can act on.
  • Handles the platform ABORTING event so an aborted run exits promptly instead of burning time.

When to use it — and when not to

ScenarioFit
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)

  1. videoUrl is 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 reports errorCode: "MISSING_VIDEO_URL" or "BAD_VIDEO_URL" on a SUCCEEDED run 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.
  2. A bare 11-character video ID is accepted. dQw4w9WgXcQ works 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 returned videoId.
  3. language is a fixed 13-value enum, lowercase ISO-639-1: en, hi, gu, pt, es, fr, de, it, ja, ko, zh, ru, ar. There is no en-US, no pt-BR, and no free-text language input.
  4. The language you ask for is not guaranteed. Read language, isTranslated, fallbackUsed, and message on the output item — not just your own request. A successful run can legitimately return a different language than requested.
  5. translate: false does not fail the run. It disables auto-translation; the Actor then returns the video's available caption language with fallbackUsed: true and a message listing the available languages. Branch on fallbackUsed, not on run status.
  6. 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" and errorCode: "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 return SUCCEEDED with exactly one dataset item whose status is "no_transcript", plus an errorCode and a plain-English message. Always branch on item["status"], not on run status — a SUCCEEDED run does not guarantee a transcript. A missing or unparseable videoUrl is reported the same way, as MISSING_VIDEO_URL or BAD_VIDEO_URLno input mistake fails the run.
  7. 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.
  8. One run = one video = one dataset item. The transcript is a nested searchResult array inside that single item, not one item per segment. itemCount: 1 is a full result.
  9. start and dur are strings, not numbers — e.g. "3.200". Cast before doing arithmetic.
  10. deliveryMode defaults to summary, not none. For dataset-only runs, either leave mcpConnector empty or set deliveryMode: "none" explicitly.
  11. perSegment delivery issues one connector call per segment. A 20-minute video can be hundreds of calls — prefer chunked or summary unless 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: none
platform: "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

FieldTypeRequiredDefaultAllowed Values / FormatExample
videoUrlstring✅ Yesnone (Console prefill only)Any public YouTube URL shape, or a bare 11-char IDhttps://youtu.be/dQw4w9WgXcQ
languagestring (enum)⬜ Noen13 lowercase codes (see below)hi
translateboolean⬜ Notruetrue / falsefalse
proxyConfigurationobject⬜ No{ useApifyProxy: true, apifyProxyGroups: ["RESIDENTIAL"] }Apify proxy editor objectsee below

Accepted URL shapes

ShapeExample
Standard watchhttps://www.youtube.com/watch?v=dQw4w9WgXcQ
Short linkhttps://youtu.be/dQw4w9WgXcQ
Shortshttps://www.youtube.com/shorts/dQw4w9WgXcQ
Embedhttps://www.youtube.com/embed/dQw4w9WgXcQ
Livehttps://www.youtube.com/live/dQw4w9WgXcQ
Mobile / music / nocookie hostshttps://m.youtube.com/watch?v=dQw4w9WgXcQ
Bare video IDdQw4w9WgXcQ

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

SituationWhat you getisTranslatedfallbackUsedmessage
Native caption exists in requested languageThat trackfalsefalsenull
No native track, translate: trueYouTube auto-translation to your languagetruefalseexplains the translation
Translation returns no textVideo's own caption languagefalsetrueexplains the failed translation
No native track, translate: falseVideo's own caption languagefalsetruelists the available languages
No language passed at allVideo's default caption trackfalsefalsenull
No caption tracks at allstatus: "no_transcript", errorCode: "NO_CAPTIONS" — run still succeedsfalsefalseexplains that the video has no captions

MCP Delivery Configuration (Optional)

ParameterTypeDefaultDescription
mcpConnectorstring""Authorized MCP connector on your Apify account (Notion, Slack, Jira, GitHub, Linear, Airtable, …). Leave empty for dataset-only runs.
deliveryModeenumsummarysummary (one call, full transcript) · chunked (split long transcripts) · perSegment (one call per segment) · none.
mcpToolstring""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.
mcpArgumentsobject{}Arguments passed to the tool. String leaves support {placeholders}.
mcpMessageTemplatestring""Template rendered and exposed to mcpArguments as {message}.

Placeholders by mode:

ModePlaceholders
summary{videoUrl}, {videoId}, {language}, {segmentCount}, {transcript} (full text), {message}
chunkedthe 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.

FieldTypeNullableToggle / controlled byMeaning
videoUrlstringNovideoUrlNormalized https://www.youtube.com/watch?v=<id> URL — not necessarily the URL you passed
videoIdstringNovideoUrlExtracted 11-character video ID — verify this matches the video you meant
requestedLanguagestringNo (may be "")languageThe language code you asked for, trimmed — echoed back verbatim
languagestringNo (may be "")language + translateThe language actually returned. "" when no transcript was found
isAutoGeneratedbooleanNo— (source track)true when the source track is YouTube's ASR (auto-generated) captions
isTranslatedbooleanNotranslatetrue when the text is a YouTube auto-translation
fallbackUsedbooleanNolanguage + translatetrue when the requested language could not be honoured
messagestringYes (null)language + translatePlain-English note about any language/translation fallback, or why no transcript exists; null on a clean match
segmentCountintegerNoNumber of segments in searchResult; 0 when status is "no_transcript"
searchResultarrayNo (may be [])Transcript segments, in playback order; [] when status is "no_transcript"
searchResult[].startstringNo (may be "")Segment start in seconds, as a string — e.g. "3.200"
searchResult[].durstringNo (may be "")Segment duration in seconds, as a string — e.g. "2.900"
searchResult[].textstringNoSegment text, HTML-unescaped, newlines collapsed to spaces. Empty segments are dropped.
statusstringNo— (video state)"ok" when a transcript was extracted, "no_transcript" when none could be — branch on this first
errorCodestringYes (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:

statuserrorCodesegmentCountsearchResultmessage
"ok"null≥ 1populatednull, 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_ERROR0[]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; segmentCount gives its length.
  • status: "no_transcript" → the run worked, the video didn't yield captions. Read errorCode and message for the reason, and the run's terminal status message shows No 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 os
from apify_client import ApifyClient
client = 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'
# 1. Start execution with a hard spend limit
curl -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. Poll
curl -H 'Authorization: Bearer <YOUR_APIFY_TOKEN>' \
'https://api.apify.com/v2/actor-runs/<runId>'
# 3. Retrieve when SUCCEEDED
curl -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 CodeDetection ConditionUnderlying CauseDeterministic Agent Action
AUTH_INVALIDHTTP 401Missing or malformed APIFY_TOKENAbort & Prompt User: request a valid API token.
RATE_LIMITEDHTTP 429Apify API rate / concurrency limitsRetry: exponential backoff (2s, 4s, 8s).
SYNC_TIMEOUTHTTP 408 on the sync endpointRun exceeded the 300s synchronous ceilingReconfigure: switch to async POST /runs + polling.
MISSING_VIDEO_URLSUCCEEDED, 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_URLSUCCEEDED, item errorCode: "BAD_VIDEO_URL", status: "no_transcript"; the offending value is echoed in videoUrlNot a YouTube URL / no 11-char ID presentModify Input: pass a clean watch URL, youtu.be link, or bare ID.
WRONG_VIDEOSUCCEEDED but videoId ≠ intended videoLast-resort 11-char token match latched onto the wrong substringValidate & Re-run: always compare returned videoId against your input.
VIDEO_UNAVAILABLESUCCEEDED, item errorCode: "VIDEO_UNAVAILABLE"Video is private, deleted, region-blocked, or the ID is wrong — both fetch paths report it unavailableDo not retry. Verify the video opens in a browser; ask the user for a valid URL.
NO_CAPTIONSSUCCEEDED, item errorCode: "NO_CAPTIONS"Video exists but has no caption tracks at allDo not retry. Route to an ASR/Whisper service; this Actor cannot transcribe audio.
CAPTIONS_BLOCKEDSUCCEEDED, item errorCode: "CAPTIONS_BLOCKED"YouTube challenged the request (bot wall, sign-in, age gate)Reconfigure & retry once: enable the RESIDENTIAL proxy group.
CAPTIONS_UNREACHABLESUCCEEDED, item errorCode: "CAPTIONS_UNREACHABLE"Neither resolution path returned usable caption metadataRetry once with a residential proxy; if it repeats, treat as unavailable.
EMPTY_TRANSCRIPTSUCCEEDED, item errorCode: "EMPTY_TRANSCRIPT"Caption track resolved but parsed to zero usable textRetry: re-run with translate: true or a different language.
UNEXPECTED_ERRORSUCCEEDED, item errorCode: "UNEXPECTED_ERROR"An unhandled upstream shape or network fault; the log carries the stack traceRetry once, then report the run ID.
PROXY_UNAVAILABLELog Could not set up proxy configuration or Running without a proxyResidential groups require a paid Apify planPrompt User: upgrade the plan; expect bot-wall failures until then.
LANGUAGE_FALLBACKSUCCEEDED with fallbackUsed: trueRequested language unavailableNot an error. Read language and message; re-run with translate: true if a translation is acceptable.
TRANSLATION_FALLBACKSUCCEEDED with isTranslated: false after requesting a foreign languageTranslation attempt returned no textNot an error. Consume the returned language or translate downstream.
DELIVERY_SKIPPEDLog no tool name was providedmcpConnector set but mcpTool emptyModify Input: set mcpTool; the dataset item is already saved.
DELIVERY_TOOL_UNKNOWNLog Tool 'x' is not available on this connectorWrong tool name for that connectorModify Input: pick a name from the Available tools: list in the same log line.
DELIVERY_EMPTYConnector called, body emptymcpArguments omitted the {message} placeholderModify Input: map {message} inside mcpArguments.
MCP_PROXY_MISSINGLog APIFY_MCP_PROXY_URL is not setRunning locally instead of on the platformReconfigure: apify push and run on the platform.
CHARGE_LIMITRun aborted immediately, before any workmaxTotalChargeUsd set below what the run costs to startPrompt User: raise the cap. Check the Store page for the current rate.

Provenance of this table: every errorCode, status value 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 saysIntentConstructed 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 extraction12 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 maxTotalChargeUsd on 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.

ScenarioRunsRelative cost per runNotes
One video, captions present1BaselineThe normal case.
One video, 3-hour lecture1≈ BaselineLength barely matters — the caption track is one fetch.
One video, primary path challenged1Slightly above baselineFallback path adds one request.
One video, no captions at all1Below baselineEnds early with NO_CAPTIONS; no parsing work.
Same video in 3 languages33 × baselineEach language is a separate run.
50 unrelated videos5050 × baselineLoop from your orchestrator.
A whole channel1➡️ 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 seeMost likely causeFix
1SUCCEEDED 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 agreedOpen 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.
2SUCCEEDED but errorCode: "CAPTIONS_BLOCKED" / "CAPTIONS_UNREACHABLE"YouTube challenged the request, or returned no caption metadata on either pathEnable RESIDENTIAL Apify Proxy (the default) and retry once. Datacenter/local IPs are blocked routinely.
3SUCCEEDED but errorCode: "NO_CAPTIONS"The video genuinely has no caption trackExpected. This Actor reads captions; it does not transcribe audio. Use an ASR/Whisper service.
3aSUCCEEDED but errorCode: "EMPTY_TRANSCRIPT"Caption track resolved but every segment parsed emptyRe-run with translate: true, or request a different language.
3bYour pipeline treats a no_transcript run as a transcriptYou checked run status instead of item["status"]Branch on item["status"] == "ok" before reading searchResult.
4Wrong language in the outputRequested language had no native track; you got a translation or a fallbackRead language, isTranslated, fallbackUsed, message. Set translate: false if only a native track will do.
5translate: false but the run still succeeded in another languageBy design — it disables translation, it does not fail the runBranch on fallbackUsed: true, not on run status.
6en-US / pt-BR rejected or ignoredOnly the 13 base enum codes are validPass en, pt, … Region variants are not selectable.
7Text reads like speech-to-text with no punctuationThe source track is YouTube's auto-generated ASR captionCheck isAutoGenerated: true. Nothing to fix — that is the only track available.
8videoId is not the video you meantA last-resort regex matched an 11-char token in a malformed inputAlways pass a clean URL or bare ID, and assert the returned videoId.
9Only one dataset itemBy design — one run = one video = one item, transcript nested in searchResultRead segmentCount for length; unwind searchResult for a per-segment table.
10start / dur arithmetic failsThey are strings ("3.200"), not numbersCast to float before arithmetic.
11message is null in your consumernull means "nothing needed explaining"Test for None/null, not for "".
12Log: Running without a proxyNo proxy configured or unavailable on the planEnable Apify Proxy residential group; a paid plan is required.
13Log shows the primary resolution path failing, but the run succeededNormal — the fallback path took overNo action needed.
14Need many videos and each run is a separate chargeOne video per run by designFor a whole channel use Full Channel Transcripts Scraper; otherwise loop and cache by videoId + language.
15Slack / Notion / Jira received nothingDelivery needs both mcpConnector and mcpTool; otherwise it is skipped with a warningSet both. The dataset item is still saved — your data is never lost.
16Log: APIFY_MCP_PROXY_URL is not setYou ran locally with apify run; connectors only resolve on the platformDeploy with apify push and run on the platform.
17Connector fired, but the message body is emptymcpArguments did not reference {message}Map {message} in mcpArguments and populate mcpMessageTemplate.
18Notion rejects or truncates a long transcriptSingle-call payload exceeded block/timeout capsUse deliveryMode: "chunked" and put {part}/{partCount} in the page title.
19Every Notion page looks identical{part}/{partCount} missing from the title templateAdd them: "Transcript {videoId} (part {part}/{partCount})".
20Hundreds of connector calls fireddeliveryMode: "perSegment" sends one call per segmentSwitch to chunked or summary unless per-line records are the goal.
21Delivery fired when you only wanted a datasetdeliveryMode defaults to summary, not noneSet deliveryMode: "none" or leave mcpConnector empty.
22HTTP 401 / 403Missing, expired, or malformed APIFY_TOKENRegenerate in Apify Console → Settings → API & Integrations, send as Authorization: Bearer <token>.
23HTTP 429Apify account concurrency / rate limits — not YouTube blockingRetry with exponential backoff (2s → 4s → 8s).
24HTTP 408 on run-sync-get-dataset-itemsThe synchronous endpoint has a hard 300-second ceilingSwitch to async: POST /runs → poll /actor-runs/<runId> → fetch dataset.

Diagnostic checklist before opening an issue

  1. 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.
  2. Confirm the proxy. Residential group on, paid plan active. This is the single most common cause of caption-retrieval failures.
  3. Baseline run. videoUrl only, language: "en", translate: true, deliveryMode: "none".
  4. Check the resolved videoId in the output or the run log against the video you meant.
  5. 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

FilePurpose
.actor/actor.jsonIdentity, version, storage and dockerfile wiring
.actor/input_schema.jsonAuthoritative typed input contract — videoUrl, language enum, translate, proxy editor, deliveryMode enum
.actor/dataset_schema.jsonOutput field metadata and both Console views (Overview + unwound Transcript) — consumed by the Apify MCP server to describe results to agents
.actor/output_schema.jsonDeclares where results are stored
my_actor/main.pyVideo-ID extraction, caption resolution and parsing, language fallback, MCP delivery
my_actor/connector.pyMCP connector session handling and {placeholder} rendering

If any table on this page disagrees with the schema files, the schema files win.


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.

ActorBest forWhy pick it over this one
YouTube Full Channel Transcripts ScraperTranscripts 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 transcriptYou want a whole channel (or its latest N videos) in one run instead of orchestrating one run per video.
YouTube Scraper: Videos, Channels & CommentsVideo, channel, and comment metadata from search queries, channel URLs, video URLs, or hashtags — up to 500 videos per input and 1,000 comments per videoYou need views, likes, descriptions, channel stats, or comment threads — none of which this transcript Actor returns.
YouTube Shorts ScraperEvery Short from a channel with engagement metrics (views, likes, comments, hashtags) and optional video downloadsYou'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

ActorUse it for
Advanced Website Crawling ActorCrawl the creator's own site or show notes for clean HTML/Markdown/text context alongside the transcript
Google Maps Scraper – Business DataEnrich businesses or venues mentioned in a transcript into full business records
GOV.UK Business ScraperMatch 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?


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."
}
}
]
}