YouTube Transcript Scraper API | Captions & Subtitles
Pricing
from $9.00 / 1,000 successful video transcripts
YouTube Transcript Scraper API | Captions & Subtitles
Extract YouTube transcripts, captions, subtitles, and timestamped transcript segments from YouTube URLs, Shorts, or video IDs. No YouTube API key required. Residential proxy enabled. Use from Apify Console or API.
Pricing
from $9.00 / 1,000 successful video transcripts
Rating
0.0
(0)
Developer
Inus Grobler
Maintained by CommunityActor stats
1
Bookmarked
28
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Extract timestamped YouTube transcripts, captions, subtitles, and transcript segments from YouTube video URLs, Shorts URLs, or video IDs. This Actor is for SEO research, AI summaries, content repurposing, media monitoring, RAG pipelines, and teams that need YouTube transcript data without using the official YouTube Data API.
The Actor returns clean segment-level rows with timestamps when captions expose timing data. It uses multiple transcript sources, Apify proxy routing on cloud runs, streaming dataset writes, and per-video retries to improve reliability while keeping memory and compute cost low.
Main Use Cases
- Build YouTube caption and subtitle datasets.
- Summarize YouTube videos with LLMs.
- Analyze podcasts, webinars, interviews, courses, and creator content.
- Extract quotes, timestamps, and text segments for articles or clips.
- Feed transcript segments into search, vector databases, or RAG workflows.
- Monitor YouTube videos for brand, product, topic, or keyword mentions.
What Data You Get
Each dataset item is one transcript segment. A single video can produce many rows.
| Field | Description |
|---|---|
video_id | YouTube video ID. |
url | Canonical YouTube watch URL. |
title | Video title when metadata is enabled and available. |
channel_name | Channel name when metadata is enabled and available. |
status | found or missing. |
language | Transcript language code. |
source | Transcript source that returned the result. |
transcript_url | Source transcript URL when available. Signed source URLs can expire. |
piece_index | Segment number within the transcript. |
piece_count | Total segment count for the video. |
piece_start | Segment start time in seconds, when available. |
piece_dur | Segment duration in seconds, when available. |
text | Transcript segment text. |
word_count | Word count for this segment. |
transcript_word_count | Word count for the full transcript. |
error | Error code when a transcript is missing. |
The key-value store record OUTPUT contains totals, warnings, source timing summaries, billing details, and whether streaming output was enabled.
Input
| Field | Required | Description |
|---|---|---|
videoUrls | Yes | YouTube watch URLs, Shorts URLs, youtu.be links, or raw video IDs. |
preferredLanguages | No | Language codes to prioritize, such as en, en-us, or es. |
maxChars | No | Maximum normalized transcript characters per video. |
maxVideoRuntimeSecs | No | Per-video fallback budget from 30 to 240 seconds. Keep the 60-second default for short runs; higher values may require increasing the Actor run timeout. |
exportVideoText | No | Save one JSON text document per successfully delivered unique video; off by default. |
fetchVideoMeta | No | Adds available title and channel metadata. Leave off for lowest cost. |
useResidentialProxy | No | Opt into paid residential proxy traffic for difficult or blocked videos. It is off by default; normal runs use Apify's automatic datacenter proxy routing. |
youtubeCookies | No | Optional cookie string or Netscape cookie file content for stricter bot-check cases. Most runs do not need this. |
Example input:
{"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"preferredLanguages": ["en", "en-us"],"maxChars": 1000,"maxVideoRuntimeSecs": 60,"fetchVideoMeta": false,"useResidentialProxy": false}
Example Output
{"video_id": "dQw4w9WgXcQ","url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ","status": "found","language": "en","source": "youtubei_player_api","record_type": "transcript_piece","piece_index": 2,"piece_count": 61,"piece_start": 18.64,"piece_dur": 3.24,"text": "Transcript segment text appears here.","word_count": 5,"transcript_word_count": 487,"error": ""}
How To Run On Apify
- Open the Actor in Apify Console.
- Add one or more YouTube URLs or video IDs.
- Keep
fetchVideoMetaoff for transcript-only runs. - Run the Actor.
- Open the Dataset tab to preview, filter, or export transcript rows.
Exporting Results
Download results from the Dataset tab as JSON, JSONL, CSV, Excel, XML, or RSS. API users can read rows from the run's defaultDatasetId.
import osfrom apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("thescrapelab/Apify-YouTube-Transcript-Scraper-2-0").call(run_input={"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"preferredLanguages": ["en", "en-us"],"maxChars": 1000,"maxVideoRuntimeSecs": 60,"fetchVideoMeta": False,})if run is None:raise RuntimeError("Actor call returned no run or storage metadata")items = client.dataset((run["defaultDatasetId"] if isinstance(run, dict) else run.default_dataset_id)).list_items(clean=True).itemsfor item in items[:5]:print(item["video_id"], item["piece_start"], item["text"])
Limits And Caveats
- Transcripts depend on caption/subtitle availability for each video.
- Private, removed, region-restricted, age-restricted, live, or bot-check-protected videos may return missing rows.
- Metadata fetching adds extra requests and can increase runtime and proxy cost.
- Duplicate input videos are not re-scraped, but duplicate rows are still returned for compatibility.
- Work stops early enough to flush dataset and
OUTPUTrecords before the configured Actor timeout. Unstarted videos receive a missing-status row. transcript_urlcan be a signed source URL and may expire; use the returnedtextas the durable transcript value.- The Actor does not use the official YouTube Data API.
Pricing
The current Store price appears before each run. The Actor charges the video-transcript event once per successful unique video, even when that video produces many timestamped segment rows. Missing videos and duplicate copies are not charged as additional successful videos.
Cost-optimized settings:
- Memory:
256 MB - Timeout:
120-240 seconds, depending on batch size - Processing mode: bounded internal parallel processing with streaming dataset writes
Start with one public video and keep maxChars bounded. Larger batches, cookie-assisted retrieval, and long transcripts can increase runtime and output volume.
Troubleshooting
| Problem | What to try |
|---|---|
| No transcript found | Confirm the video has captions or subtitles available. |
| Run is slow | Keep fetchVideoMeta disabled and split very large video lists into smaller batches. |
A row reports video_runtime_budget_exhausted | Increase maxVideoRuntimeSecs for that input, up to 240 seconds, or retry the video with residential proxy enabled. |
A row reports actor_runtime_budget_exhausted | Increase the Actor run timeout or submit a smaller batch. The Actor stopped starting work so it could save results cleanly. |
| Some videos are missing | Try preferred language fallbacks such as ["en", "en-us", "es"]. |
| Bot-check errors | Provide youtubeCookies only if the target videos require authenticated access. |
| Unexpected duplicate rows | Remove duplicate video URLs from input if you only want one copy of each transcript. |
FAQ
Can this Actor scrape YouTube transcripts without a YouTube API key?
Yes. It extracts captions and transcript data from public web transcript sources and does not require a YouTube Data API key.
Does it work with YouTube Shorts?
Yes. You can provide Shorts URLs, watch URLs, youtu.be links, or raw video IDs.
Why does one video return many dataset rows?
The Actor returns timestamped transcript segments. This makes filtering, search, and downstream AI processing easier than a single large text blob.
Can I get one row per video?
The primary dataset stays segment-based. Set exportVideoText: true to also get one JSON text document per successfully delivered unique video. Read the transcriptExports manifest in OUTPUT for its record key.
What is the best low-cost setting?
Use 128 MB and keep fetchVideoMeta off unless title and channel metadata are required.
One text document per video for research and AI workflows
Set exportVideoText: true when you want text ready for downstream research, summarization or retrieval without grouping segment rows yourself. This does not call an AI model or fetch the video again.
Each successful unique video gets a JSON key-value record named TRANSCRIPT-<video_id> with video_id, canonical url, language, source, text and piece_count. Text is the delivered segments joined in timestamped piece order and remains bounded by your existing maxChars setting. The 1,000-character default is a preview and may omit later speech; increase it explicitly if you need more available caption text.
The OUTPUT record includes transcriptExports, a list of { "video_id": "...", "recordKey": "TRANSCRIPT-..." } entries. Open those records through the run's key-value store, or use the API:
# Reuse the client and run from the API example above, adding# "exportVideoText": True to that call's run_input.store = client.key_value_store(run["defaultKeyValueStoreId"])output_record = store.get_record("OUTPUT")output = output_record["value"] if output_record else {}for entry in output.get("transcriptExports", []):document = store.get_record(entry["recordKey"])["value"]print(document["video_id"], document["text"])
The default dataset, timestamps and successful-video charge stay the same. Duplicate input videos share one text export, while their existing duplicate dataset rows are preserved. Missing or charge-suppressed transcripts have no text document. An export-write failure appears in OUTPUT.warnings; already delivered dataset segments remain available. Extra key-value storage operations can incur normal platform usage.