Arcadium Data Scrapers
Home / Guides / YouTube Transcript & Subtitles Scraper

How to use YouTube Transcript & Subtitles Scraper

Step by step: in the Apify Store, over the API, from JavaScript or Python, in n8n, and from AI agents through MCP or LangChain.

$2.48 per 1,000 transcripts on the Free plan, down to $1.80 on higher plans (live Apify price on 2026-10-31).

1. In the Apify Store (no code)

  1. Open arcadium/youtube-transcripts and sign in to Apify (a free account works).
  2. Fill in the input form. The fields below are the ones the example uses.
  3. Set a maximum cost per run in the run options if you want a hard cap.
  4. Start the run. Rows appear in the Output tab as they arrive.
  5. Export the dataset as JSON, CSV or Excel, or connect an integration.
Input fieldTypeDescription
urlsarrayExample: https://www.youtube.com/watch?v=dQw4w9WgXcQ, https://youtu.be/dQw4w9WgXcQ, a Shorts URL, a bare video ID (dQw4w9WgXcQ), a channel (https://www.youtube.com/@kurzgesagt, add /shorts or /streams for those tabs) or a playlist URL. Channels and playlists give up to maxVideosPerSource videos each, channels newest first.
languagesarrayISO language codes in order of preference, e.g. ["en", "de"]; en also matches en-US and en-GB. The first one the video has is used. Empty or none available: the original spoken language (languageMatched is false when none matched).
captionTypestringValues: any (uploaded captions first, else auto-generated), manual (uploaded by the creator only), auto (YouTube speech recognition only). Videos without that type give a free error row. Default: any.
maxVideosPerSourceintegerTranscripts per channel or playlist URL, 1 to 5,000 (default 50). Videos without captions or outside the date range do not count. Direct video URLs are not limited by this.
subtitleFormatstringValues: none, srt, vtt. srt or vtt adds the captions as a ready subtitle file in the srt or vtt field. Default: none.

Example input:

{
  "urls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "languages": [],
  "captionType": "any",
  "maxVideosPerSource": 10,
  "subtitleFormat": "none"
}

2. Over the Apify API

The synchronous endpoint runs the actor and returns its rows in one call (for runs up to about five minutes; use the asynchronous run endpoint for longer ones).

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"}'

3. From 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);

4. From 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)

5. In n8n

Paste this node into a workflow (it is a plain HTTP Request node). Create a Header Auth credential with name Authorization and value Bearer <APIFY_TOKEN>; each returned row becomes one item for the next node.

{
  "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": {}
}

6. From an AI assistant (MCP)

Add Apify’s MCP server to Claude Desktop, Cursor, VS Code or any MCP client, limited to this actor:

{
  "mcpServers": {
    "apify": {
      "url": "https://mcp.apify.com/?tools=arcadium/youtube-transcripts",
      "headers": {
        "Authorization": "Bearer <APIFY_TOKEN>"
      }
    }
  }
}

More on the MCP page.

7. From 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])

Sample output

One row, from the example in the actor’s README. Long text is shortened and lists are 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"
}
FieldWhat it holds
rowTypeKind of row. Filter on this first. Values: 'transcript': one video's transcript (charged). 'error': a video or URL gave no transcript; see code and message (free).
platformAlways 'youtube'.
videoIdYouTube video id, 11 characters, e.g. dQw4w9WgXcQ.
urlWatch page link of the video (Shorts included).
titleVideo title.
channelNameName of the channel that published the video (the publisher, not a viewer).
channelIdChannel id, starts with UC, e.g. UCuAXFkgsw1L7xaCfnd5JJOw.
channelHandleChannel handle with the @, e.g. @RickAstleyYT. Null if YouTube did not return it.
channelUrlLink to the channel page built from channelId.
durationSecondsVideo length in seconds.
publishDatePublish date and time as YouTube gives it, ISO 8601 with offset, e.g. 2009-10-24T23:57:33-07:00.
viewCountViews at the time of the run.
likeCountLikes at the time of the run; null when hidden.
categoryYouTube category, e.g. Music, Education, News & Politics.
isLiveContentTrue for live streams and recorded live streams.
keywordsTags the creator set on the video (may be empty).
descriptionVideo description, short (about 300 characters) or full per input; emails and phone numbers are removed.
languageLanguage code of the returned transcript, e.g. en, de-DE, pt-BR.
languageNameEnglish name of that language, e.g. German (Germany).
isAutoGeneratedTrue when the transcript is YouTube's speech recognition (no punctuation, may misspell names); false when the creator uploaded it.
languageMatchedFalse when none of the requested languages existed and the original-language track was returned instead.
translationStatusValues: 'not_requested', 'original' (the video has a native track in translateTo), 'translated' (YouTube's own machine translation), 'unavailable' (YouTube refused it; the original is returned).
translatedFromSource language code when translationStatus is 'translated', else null.
availableLanguagesEvery caption track of the video: code, name, isAutoGenerated. Use it to request another language.
transcriptThe whole transcript as one text: caption lines joined with spaces. Emails and phone numbers are removed.
segmentCountNumber of caption lines.
wordCountWords in transcript, for chunking and token estimates.
segmentsCaption lines in order: start (seconds from the start), duration (seconds), text. Present when includeSegments is on.
srtThe captions as an SRT subtitle file, when subtitleFormat is srt.
vttThe captions as a WebVTT subtitle file, when subtitleFormat is vtt.
sourceThe channel or playlist URL this video came from; null for videos given directly.
scrapedAtWhen the row was read, ISO 8601.
inputError rows: the URL or id as given.
codeError rows: not_found (private, removed, unknown channel), no_match (no captions, or none of the requested type), unavailable (age-restricted, members-only, upcoming, temporary error), blocked (rate limited, retry later), invalid_input.
messageError rows: what happened, in plain English.

Schedule it

In Apify Console, create a Task from your input and add a Schedule (for example daily). Each run writes a new dataset; integrations can push the rows to Google Sheets, Slack, a webhook or your database.