YouTube Transcript & Subtitles Scraper
Give it YouTube video, Shorts, channel or playlist links (or plain video IDs) and it returns each transcript (subtitles, captions) as JSON: full text, timed segments, language, whether it was auto-generated, every caption language the video has, and the video's title, channel, length, publish date and views. From $2.48 per 1,000 transcripts (less on higher Apify plans); you pay only for rows you get.
$2.48 per 1,000 transcripts on the Free plan, down to $1.80 on higher plans
Why this actor
- Channel and playlist input with a date range, not just single video links.
- Agent-ready output:
languageMatched,availableLanguagesand error codes tell an agent what it got and why. - srt and vtt formats for subtitle work, plain text for RAG and LLM summaries.
- Videos without captions, private, age-restricted or missing videos and repeats are free.
Drop-in input
Drop-in input. Inputs written for pintostudio/youtube-transcript-scraper (videoUrl, targetLanguage), johnvc/YoutubeTranscripts (youtube_url, channel_url, output_formats) and starvibe/youtube-video-transcript (language, max_videos, start_date) work as they are. pintostudio charges $10 per 1,000 and 13.6% of its runs failed (Apify Store, 30 days to 2026-10-31); here it is from $2.48, and a video without captions is a free error row, not a failed run.
As stated in the actor’s README. The full list of accepted input names is on the Store page.
What you get
| Field | What it holds |
|---|---|
videoId, url, title | The video. Shorts are returned the same way. |
channelName, channelId, channelHandle, channelUrl | The channel that published it. |
durationSeconds, publishDate, viewCount, likeCount, category, isLiveContent, keywords | Basic video metadata. publishDate is ISO 8601 with YouTube's offset. |
description | Video description, short or full (your choice); emails and phone numbers removed. |
transcript | The whole transcript as one string. |
segments | Caption lines in order: start and duration in seconds, text. |
segmentCount, wordCount | For chunking and token estimates. |
language, languageName, isAutoGenerated | Which caption track you got, and whether it is YouTube speech recognition (true) or uploaded by the creator (false). |
languageMatched | false when none of your languages existed and the original-language track was returned instead. |
availableLanguages | Every caption track of the video (code, name, isAutoGenerated), so you can ask for another one. |
translationStatus, translatedFrom | Only with translateTo: original, translated or unavailable (see Limitations). |
srt, vtt | Ready subtitle files when subtitleFormat is srt or vtt. |
source | The channel or playlist URL the video came from. |
scrapedAt | When the row was read. |
Sample row (from the README example; long text shortened, lists cut to two items):
{
"rowType": "transcript",
"platform": "youtube",
"videoId": "dQw4w9WgXcQ",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
"channelName": "Rick Astley",
"channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
"channelHandle": "@RickAstleyYT",
"channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw",
"durationSeconds": 213,
"publishDate": "2009-10-24T23:57:33-07:00",
"viewCount": 1829055386,
"likeCount": 19491653,
"category": "Music",
"isLiveContent": false,
"keywords": [
"rick astley",
"Never Gonna Give You Up"
],
"description": "The official video for “Never Gonna Give You Up” by Rick Astley. Never: The Autobiography OUT NOW!...",
"language": "en",
"languageName": "English",
"isAutoGenerated": false,
"languageMatched": true,
"translationStatus": "not_requested",
"translatedFrom": null,
"availableLanguages": [
{
"code": "en",
"name": "English",
"isAutoGenerated": false
},
{
"code": "en",
"name": "English",
"isAutoGenerated": true
}
],
"transcript": "[♪♪♪] ♪ We're no strangers to love ♪ ♪ You know the rules and so do I ♪ ...",
"segmentCount": 61,
"wordCount": 487,
"segments": [
{
"start": 1.36,
"duration": 1.68,
"text": "[♪♪♪]"
},
{
"start": 18.64,
"duration": 3.24,
"text": "♪ We're no strangers to love ♪"
}
],
"source": null,
"scrapedAt": "2026-10-22T08:00:00.000Z"
}
Price
Live Apify price on 2026-10-31, per 1,000 rows, by Apify plan. You pay only for rows delivered; error and summary rows marked free in the README cost nothing. Plus $0.001 per GB of memory when a run starts.
| Per 1,000 | Free | Bronze | Silver | Gold | Platinum | Diamond |
|---|---|---|---|---|---|---|
| Transcript (main) | $2.48 | $2.25 | $2.03 | $1.80 | $1.80 | $1.80 |
Use cases
- RAG and knowledge bases: index a channel's or a course playlist's transcripts with timestamps for citations.
- Summaries and notes: feed
transcriptto an LLM;wordCounttells you how many tokens to expect. - Content and competitor research: what a channel talked about this month (
publishedAfter). - Subtitles: download
srtorvttin the language you need. - Agents: "watch" a video for the user by reading its transcript.
Run it from code or an AI agent
Actor ID arcadium/youtube-transcripts. Set APIFY_TOKEN to your Apify API token. The input below is the actor’s prefilled example; see the guide for every field.
curl
curl -X POST "https://api.apify.com/v2/acts/arcadium~youtube-transcripts/run-sync-get-dataset-items" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"urls":["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],"languages":[],"captionType":"any","maxVideosPerSource":10,"subtitleFormat":"none"}'JavaScript
// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('arcadium/youtube-transcripts').call({
"urls": [
"https://www.youtube.com/watch?v=dQw4w9WgXcQ"
],
"languages": [],
"captionType": "any",
"maxVideosPerSource": 10,
"subtitleFormat": "none"
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);Python
# pip install apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("arcadium/youtube-transcripts").call(run_input={
"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
"languages": [],
"captionType": "any",
"maxVideosPerSource": 10,
"subtitleFormat": "none",
})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)MCP
{
"mcpServers": {
"apify": {
"url": "https://mcp.apify.com/?tools=arcadium/youtube-transcripts",
"headers": {
"Authorization": "Bearer <APIFY_TOKEN>"
}
}
}
}LangChain
# pip install langchain-apify (reads APIFY_API_TOKEN from the environment)
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("arcadium/youtube-transcripts")
rows = tool.invoke({"run_input": {
"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
"languages": [],
"captionType": "any",
"maxVideosPerSource": 10,
"subtitleFormat": "none",
}})
# or give `tool` to an agent: create_react_agent(llm, [tool])LlamaIndex
# pip install llama-index-readers-apify
import os
from llama_index.core import Document, VectorStoreIndex
from llama_index.readers.apify import ApifyActor
reader = ApifyActor(os.environ["APIFY_TOKEN"])
documents = reader.load_data(
actor_id="arcadium/youtube-transcripts",
run_input={
"urls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
"languages": [],
"captionType": "any",
"maxVideosPerSource": 10,
"subtitleFormat": "none",
},
dataset_mapping_function=lambda item: Document(text=str(item.get("transcript") or ""), metadata={"url": item.get("url")}),
)
index = VectorStoreIndex.from_documents(documents)CrewAI
# pip install crewai crewai-tools langchain-apify (reads APIFY_API_TOKEN from the environment)
from crewai import Agent
from crewai_tools import ApifyActorsTool
tool = ApifyActorsTool(actor_name="arcadium/youtube-transcripts")
researcher = Agent(
role="Research analyst",
goal="Answer with data collected by the tool",
backstory="Uses public web data.",
tools=[tool],
)n8n
{
"nodes": [
{
"name": "Run arcadium/youtube-transcripts",
"type": "n8n-nodes-base.httpRequest",
"typeVersion": 4,
"position": [
0,
0
],
"parameters": {
"method": "POST",
"url": "https://api.apify.com/v2/acts/arcadium~youtube-transcripts/run-sync-get-dataset-items",
"authentication": "genericCredentialType",
"genericAuthType": "httpHeaderAuth",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"urls\":[\"https://www.youtube.com/watch?v=dQw4w9WgXcQ\"],\"languages\":[],\"captionType\":\"any\",\"maxVideosPerSource\":10,\"subtitleFormat\":\"none\"}",
"options": {
"timeout": 300000
}
}
}
],
"connections": {}
}Google Sheets
// Extensions > Apps Script. Store your token once: Project Settings > Script properties > APIFY_TOKEN.
function importLastRun() {
const token = PropertiesService.getScriptProperties().getProperty('APIFY_TOKEN');
const url = 'https://api.apify.com/v2/acts/arcadium~youtube-transcripts/runs/last/dataset/items?status=SUCCEEDED&clean=true&format=json';
const res = UrlFetchApp.fetch(url, { headers: { Authorization: 'Bearer ' + token } });
const rows = JSON.parse(res.getContentText());
if (!rows.length) return;
const keys = Object.keys(rows[0]);
const values = rows.map((r) => keys.map((k) => (typeof r[k] === 'object' ? JSON.stringify(r[k]) : r[k])));
const sheet = SpreadsheetApp.getActiveSheet();
sheet.clearContents();
sheet.getRange(1, 1, 1, keys.length).setValues([keys]);
sheet.getRange(2, 1, values.length, keys.length).setValues(values);
}Limitations
- Only captions YouTube already has (uploaded or auto-generated). Videos without any captions give a free
no_matchrow; the actor does not download or transcribe audio. - Auto-generated transcripts have no punctuation and can misspell names.
- Translation: YouTube currently refuses its machine-translated tracks to anonymous clients. With
translateTo, a video that has a native track in that language returns it (translationStatus: "original", common for big channels and TED talks, which carry dozens of languages); otherwise you get the original language withtranslationStatus: "unavailable". - Age-restricted and members-only videos need a signed-in account and are skipped (free
unavailablerow). Premieres and upcoming streams have no captions yet. - Live streams: captions of a running live stream are not available; recorded streams work once YouTube has processed them.
FAQ
Which language do I get? The first of your languages that the video has, uploaded captions before auto-generated ones. With no languages, the original spoken language (the language of the auto-generated track); a video without an auto-generated track gives YouTube's default track, usually English when there is one. availableLanguages lists everything the video offers.
A video returned no transcript. Why? Look at its error row: no_match means the video has no captions (or none of the captionType you asked for), not_found that it is private or removed, unavailable that it is age-restricted, members-only or not published yet. These rows are free.
How fast is it? About 1 to 1.5 seconds per video, 4 videos in parallel. 100 transcripts take one to two minutes.
Something looks wrong in the output. Who do I tell? Open an issue on the actor page with the run link. We read them all.
Related actors
YouTube Scraper
YouTube videos and Shorts from channels, playlists, searches or links, $3.60 per 1,000; streamers/youtube-scraper input works as is.
$3.60 per 1,000 videos on the Free plan, down to $2.16 on higher plans