# YouTube Transcript, Subtitles & Comments Scraper (`aaronoptimus/youtube-transcripts-scraper`) Actor

YouTube transcript scraper & API: get subtitles & captions as plain text, SRT, VTT, timestamped JSON or LLM-ready chunks from videos, Shorts, channels, playlists and search. Any language or translated, plus chapters, metadata and comments. $2 per 1,000 transcripts; videos without captions are free.

- **URL**: https://apify.com/aaronoptimus/youtube-transcripts-scraper.md
- **Developed by:** [Aaron Tuttle](https://apify.com/aaronoptimus) (community)
- **Categories:** Videos, AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.60 / 1,000 video transcript delivereds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## YouTube Transcript Scraper & API

Get **YouTube transcripts** (subtitles / closed captions) as **plain text, clean paragraphs, SRT, WebVTT, timestamped JSON or LLM-ready chunks**, from **videos, Shorts, channels, playlists and YouTube search**. Any caption language, an ordered list of preferred languages, or YouTube's own translation. Chapters, video metadata and optional **comments with replies** are included.

**$2 per 1,000 transcripts, from $1.60 on higher plans.** Videos without captions are free, searching and listing channels is free, and there's no extra platform fee: compute and proxies are included.

YouTube blocks anonymous transcript requests aggressively, which is why transcript tools often hang, return "Sign in to confirm you're not a bot" or come back empty. This scraper retries blocked requests on fresh sessions and IPs, has several independent ways to download a transcript, and only charges for transcripts it actually delivers.

### Quick start

1. Paste one or more **YouTube URLs** (or type **search terms** under *Search YouTube*).
2. Optionally pick **transcript languages**, a **chunk size** for AI pipelines, or turn on **comments**.
3. Click **Start** and download the results as JSON, CSV or Excel, or read them through the API.

The smallest input that works:

```json
{ "startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] }
```

### What you can paste

| Input | Examples | What happens |
|---|---|---|
| Video | `https://www.youtube.com/watch?v=…`, `https://youtu.be/…`, `/shorts/…`, `/live/…`, `/embed/…`, or an 11-character ID | One item per video |
| Channel | `@handle`, `https://www.youtube.com/@handle`, `/channel/UC…`, `/c/…`, `/user/…` | Newest videos first, up to *Max videos per channel/playlist*; optionally Shorts and finished live streams too |
| Playlist | `https://www.youtube.com/playlist?list=PL…` | Videos in playlist order |
| Search | Terms under *Search YouTube* (`searchQueries`), or a `youtube.com/results?search_query=…` URL | Video results, up to *Max videos per search*, with YouTube's sort and upload-date filters |

Expanding channels, playlists and searches is free; you pay per transcript delivered. Duplicates across sources are processed once.

**Search options:** `searchSort` (`relevance` or `popularity`), `searchUploadDate` (`any`, `today`, `week`, `month`, `year`) and `searchSubtitlesOnly` (YouTube's "Subtitles/CC" filter). YouTube no longer offers sorting search results by upload date, so use the upload-date filter or *Published after* for recent videos. Without *Include Shorts*, searches ask YouTube for regular videos only. Videos that are live right now or scheduled have no transcript yet and are left out.

### Output formats

**Items** (default): one JSON record per video.

| Field | What it is |
|---|---|
| `transcript.text` | The whole transcript in one string |
| `transcript.paragraphText` | The same text split into readable paragraphs (at chapters, pauses and sentence ends) |
| `transcript.chunks` | With *Chunk length* (`chunkSeconds`) or *Chunk size* (`chunkWords`): `[{ index, startSec, endSec, text, wordCount, url, chapter }]`, where `url` opens the video at that moment (`&t=…s`). Chunks never cross a chapter |
| `transcript.segments` | Timed captions: `[{ start, duration, text }]` |
| `transcript.srt`, `transcript.vtt` | Ready-to-save subtitle files |
| `transcript.language`, `isAutoGenerated`, `isTranslated`, `sourceLanguage`, `availableLanguages` | Which captions you got and which exist |
| `chapters` | Title, start and end of each chapter (YouTube's markers, else timestamps in the description) |
| Metadata | `title`, `channel`, `channelId`, `publishedAt`, `durationSec`, `viewCount`, `likeCount`, `description`, `tags`, `category`, `thumbnails`, live / unlisted flags |
| `comments` | With *Comments* on (see below) |
| `hasTranscript`, `reason`, `warnings` | Why a video has no transcript, and notes such as a language fallback |

Turn on **Lean items** (`leanOutput`) to leave out `srt`, `vtt` and `segments` so records stay small for LLM context windows; text, paragraphs, chunks, chapters and metadata stay.

Example (shortened):

```json
{
  "type": "video",
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video)",
  "channel": "Rick Astley",
  "publishedAt": "2009-10-25T06:57:33.000Z",
  "durationSec": 213,
  "hasTranscript": true,
  "transcript": {
    "language": "en",
    "isAutoGenerated": false,
    "isTranslated": false,
    "segmentCount": 61,
    "text": "We're no strangers to love You know the rules and so do I ...",
    "paragraphText": "We're no strangers to love You know the rules and so do I ...\n\n...",
    "chunks": [{ "index": 0, "startSec": 18.64, "endSec": 43.2, "text": "We're no strangers to love ...", "wordCount": 52, "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&t=18s", "chapter": null }],
    "srt": "1\n00:00:18,640 --> 00:00:21,480\nWe're no strangers to love\n\n...",
    "segments": [{ "start": 18.64, "duration": 2.84, "text": "We're no strangers to love" }]
  },
  "chapters": [],
  "comments": [],
  "warnings": []
}
```

A video without a transcript is delivered like this and **not charged**:

```json
{ "type": "video", "videoId": "…", "hasTranscript": false, "reason": "no-captions", "error": "This video has no captions (neither uploaded nor auto-generated).", "transcript": null }
```

`reason` is one of `no-captions`, `live-no-captions`, `language-unavailable`, `private`, `unavailable`, `age-restricted`, `members-only`, `geo-blocked`, `premium-only`, `live-offline`. A video that YouTube kept blocking after all retries is an item with `type: "error"` (also not charged).

**Rows** (`outputFormat: "rows"`): flat rows for CSV, Excel and Google Sheets, with the same columns on every row. `recordType` is `video`, `transcript_segment` (or `transcript_chunk` when chunking is on), `comment` or `error`; transcript rows carry `startSec`, `endSec`, `text`, `chapter` and a `timestampUrl` link to the moment.

The dataset has ready-made views: **Overview**, **Transcript segments**, **AI chunks**, **Clean text (paragraphs)**, **Comments** and **Flat rows**.

### How much does it cost to scrape YouTube transcripts?

You pay per result; compute and proxy costs are included. Your Apify plan decides your discount tier, and higher tiers get lower prices automatically:

| Event | Free / Bronze | Silver | Gold and above |
|---|---|---|---|
| Transcript delivered (per 1,000) | $2.00 | $1.80 | $1.60 |
| Comment delivered (per 1,000) | $0.50 | $0.45 | $0.40 |
| Run start (once per run at the default 256 MB memory) | $0.00005 | $0.00005 | $0.00005 |

| Example (Free / Bronze prices) | Approx. cost |
|---|---|
| 1 video transcript | $0.002 |
| 100 transcripts (e.g. a channel's latest 100 videos) | $0.20 |
| 1,000 transcripts | $2.00 ($1.60 on Gold and above) |
| 1 transcript + 100 comments | $0.052 |
| 50 transcripts + 100 comments each | $2.60 |

**Not charged:** videos without captions, private, removed, age-restricted, members-only or still-live videos, transcripts skipped by your language setting, videos left out by *Published after* or *Skip already scraped*, videos YouTube kept blocking, and the searching and listing itself. A charge is one delivered transcript, whatever formats, chunks or translation you asked for. To cap a run, set Apify's *Maximum cost per run*; the scraper stops cleanly at the limit.

**Free Apify plan:** a free $5 credit ≈ 2,500 transcripts at $2 per 1,000. Free-plan runs process up to **100 videos** and **50 comments per video**; the run finishes normally and the log says what was skipped. Paid plans have no per-run cap.

### YouTube comments

Turn on **Comments** (`includeComments`) to get top or newest comments (`commentSort`), optionally with **replies** (`includeReplies`), up to *Max comments per video* (replies count toward it). Each comment has `commentId`, `author`, `authorChannelId`, `text`, `likes`, `replyCount`, `publishedTime` (YouTube's text, e.g. "2 weeks ago"), an approximate `publishedAt`, `isPinned`, `isHearted`, `isCreator` and, for replies, `parentCommentId`. You can collect comments without transcripts by turning *Transcript* off. Comments are charged per comment delivered; if comments are turned off for a video, `commentsDisabled` is `true` and nothing is charged for them.

### Languages & translation

- **Transcript languages** (`languages`): an ordered list such as `["en", "es"]`. The first language the video has is used, uploaded captions before auto-generated ones, exact code (`pt-BR`) before the base language (`pt`). The older single `language` field still works.
- **If none of these languages exists** (`whenLanguageMissing`):
  - `original` (default): the video's own captions, with a note in `warnings`;
  - `translate`: YouTube's translation of the original captions into your first language;
  - `skip`: no transcript (`reason: "language-unavailable"`), not charged.
- **Always translate to** (`translateTo`): translate every transcript into one language with YouTube's translation (the same as the website's "auto-translate"). `transcript.sourceLanguage` tells you what it was translated from.
- Leave the languages empty to get each video's original language. `transcript.availableLanguages` lists every caption track a video has.

### Use with AI agents (MCP, n8n, Make, Zapier)

**MCP (Claude, Cursor, VS Code and other MCP clients):** add Apify's MCP server with just this Actor as a tool. Hosted server (OAuth or an `Authorization: Bearer <APIFY_TOKEN>` header):

```json
{
  "mcpServers": {
    "youtube-transcripts": {
      "url": "https://mcp.apify.com?tools=aaronoptimus/youtube-transcripts-scraper"
    }
  }
}
```

Local server (stdio):

```json
{
  "mcpServers": {
    "youtube-transcripts": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--tools", "aaronoptimus/youtube-transcripts-scraper"],
      "env": { "APIFY_TOKEN": "YOUR_APIFY_TOKEN" }
    }
  }
}
```

Tip for agents: ask for `"leanOutput": true` and `"chunkWords": 300` to get compact, citeable chunks instead of full subtitle files.

**n8n, Make, Zapier:** use Apify's integration for your tool, choose this Actor, pass the same JSON input, then read the run's dataset items. You can also schedule runs and send results to Google Sheets, Slack or webhooks with Apify integrations.

**API / code** (JavaScript):

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: 'MY_APIFY_TOKEN' });
const run = await client.actor('AaronOptimus/youtube-transcripts-scraper').call({ searchQueries: ['how to start a podcast'], maxVideosPerSearch: 10, chunkWords: 300, leanOutput: true });
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const v of items) console.log(v.title, v.transcript?.chunks?.length);
```

Python:

```python
from apify_client import ApifyClient

client = ApifyClient("MY_APIFY_TOKEN")
run = client.actor("AaronOptimus/youtube-transcripts-scraper").call(run_input={"startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "languages": ["en", "es"]})
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items[0]["transcript"]["text"])
```

#### Scheduled channel monitoring

Schedule a run (Apify → Schedules) with an input like this to get only new uploads and pay only for them:

```json
{
  "startUrls": ["https://www.youtube.com/@TED"],
  "publishedAfter": "7 days",
  "includeShorts": false,
  "includeLiveStreams": true,
  "skipAlreadyScraped": true,
  "skipAlreadyScrapedStore": "ted-monitor"
}
```

- **Published after** (`publishedAfter`): a date (`2026-09-01`) or an age (`"7 days"`, `"24 hours"`, `"2 weeks"`). Applies to channels, playlists and searches; videos you paste directly are always processed. Paging stops once a channel's videos are older, and each video's own publish date is checked before its transcript is downloaded.
- **Include Shorts** / **Include live streams**: add a channel's Shorts tab and its finished live streams (Live tab). *Max videos per channel/playlist* applies to each tab.
- **Skip videos already scraped** (`skipAlreadyScraped`, off by default): IDs of delivered transcripts are kept in a named key-value store in your Apify account (`skipAlreadyScrapedStore`, default `youtube-transcripts-seen`) and skipped in later runs. Videos that had no transcript yet are tried again next time, since YouTube adds auto-generated captions some time after upload. Use a different store name per schedule to keep separate lists, or delete the store to start over.

### Is there an official YouTube transcript API?

YouTube's official Data API v3 can list a video's caption tracks, but downloading caption text with it requires OAuth and is meant for videos you own or are allowed to edit, so it doesn't give you transcripts of arbitrary public videos. That is why transcript tools read the same captions the YouTube website and apps show. This Actor gives you that as an API: one HTTP call (or an MCP tool call) with video, channel, playlist or search input, and JSON, CSV or Excel back.

### youtube-transcript-api blocked on AWS/VPS?

The popular open-source Python library `youtube-transcript-api` works well on a laptop, but YouTube blocks most requests from cloud servers (AWS, GCP, Azure, VPS providers), so it fails with "request blocked" / IP-blocked errors once deployed. Running it behind your own rotating proxies is the usual workaround. This Actor does that part for you: it runs on Apify Proxy with automatic session and IP rotation, switches to residential proxy for a video only when the default proxy keeps being refused, and returns a clear error item (not charged) for videos it still can't get. No proxy setup or extra proxy bill on your side.

### FAQ

**Why do other transcript tools hang or return nothing?** YouTube serves transcripts only to sessions it trusts and answers the rest with sign-in walls, HTTP 429s or empty caption files. Tools that try once either stall or return empty results. This scraper retries on fresh sessions and IPs with short backoff, tries several transcript sources (the caption-track download, the Android app's caption tracks and, when available, the watch page's transcript panel), and gives each video a time limit so a run never hangs.

**Which videos have no transcript?** Videos whose creator didn't upload captions and that YouTube hasn't auto-captioned (common for music, very new uploads and some languages), ongoing live streams and premieres, and videos YouTube won't serve anonymously (private, removed, age-restricted, members-only). They're delivered with `hasTranscript: false` and a `reason`, and not charged.

**Are auto-generated transcripts accurate?** They're YouTube's own speech recognition, the same text as in the transcript panel. `transcript.isAutoGenerated` tells you which kind you got; uploaded captions are used whenever they exist in your language.

**Do Shorts have transcripts?** Many do (usually auto-generated). Paste Shorts URLs directly, or turn on *Include Shorts* for channels and searches.

**How do I get the most recent videos on a topic?** Search with `searchUploadDate: "week"` (or `today` / `month`), or combine search terms with *Published after*.

**What does a "transcript" charge include?** Everything for that video: text, paragraphs, chunks, SRT, VTT, segments, chapters and metadata, in any language or translated. Comments are charged separately, per comment.

**What happens if YouTube blocks a request?** The scraper retries on a fresh session and IP and, if needed, switches to residential proxy for that video. If a video still fails within its time limit, it's listed with `type: "error"` and not charged. If only the comments fail, you still get the transcript plus a note in `warnings`.

**Is it legal?** The scraper only reads publicly available data that any visitor can see without logging in: captions, metadata and comments. It doesn't log in, bypass age or membership restrictions, solve CAPTCHAs or download videos. Comments contain user names, which may be personal data in your jurisdiction; you're responsible for using the data in line with YouTube's terms and your local laws (GDPR, CCPA…).

### Limitations

- Only captions that exist on YouTube (uploaded or auto-generated) are returned; there is no speech-to-text for videos without captions.
- Private, members-only and age-restricted videos can't be read anonymously; ongoing live streams get a transcript only after they end.
- YouTube's search returns a limited number of results per query; very broad terms may end before *Max videos per search*.
- Channel and search dates shown by YouTube are relative ("2 weeks ago"), so *Published after* uses a lenient first pass and then each video's own publish date (day precision for older videos).
- `publishedAt` of comments is approximate (YouTube only shows relative times).
- Free Apify plan: up to 100 videos per run and 50 comments per video.

Found a problem or need a feature? Open an issue on the **Issues** tab. See the **Changelog** for updates.

# Changelog

This Actor's version history is a separate document: https://apify.com/aaronoptimus/youtube-transcripts-scraper/changelog.md

# Actor input Schema

## `startUrls` (type: `array`):

Video URLs (watch, youtu.be, Shorts, live, embed), channel URLs (@handle, /channel/UC…, /c/…, /user/…), playlist URLs, search result URLs (youtube.com/results?search_query=…) or bare 11-character video IDs — one per line. Channels, playlists and searches are expanded to their videos for free. Free plan: 100 videos per run.

## `searchQueries` (type: `array`):

Search terms, one per line, e.g. "how to start a podcast". Each search is expanded to its video results (see "Max videos per search"); the search itself is free, you pay per transcript delivered.

## `maxVideosPerSearch` (type: `integer`):

How many video results to take from each search term. YouTube serves about 20 results per page; the scraper pages through them until it has this many (or YouTube has no more).

## `searchSort` (type: `string`):

YouTube's own ordering: relevance, or popularity (most viewed first). YouTube no longer offers sorting by upload date; use "Search: uploaded within" or "Published after" to get recent videos.

## `searchUploadDate` (type: `string`):

YouTube's upload-date filter for searches.

## `searchSubtitlesOnly` (type: `boolean`):

Apply YouTube's "Subtitles/CC" search filter. YouTube applies it to uploaded subtitles, so videos that only have auto-generated captions may be left out.

## `maxVideosPerSource` (type: `integer`):

How many videos to take from each channel (newest first) or playlist. With "Include Shorts" / "Include live streams", this limit applies to each of a channel's Videos, Shorts and Live tabs. Listing is free; you pay per transcript delivered.

## `includeTranscript` (type: `boolean`):

Get the transcript (subtitles / captions) as plain text, paragraphs, SRT, WebVTT and timed segments. Videos without captions are delivered with hasTranscript: false and are not charged.

## `languages` (type: `array`):

Caption language codes to look for, in order, e.g. en, es, pt-BR. The first one the video has is used (uploaded captions before auto-generated ones). Leave empty for the video's original language. (The older single "language" field is still accepted.)

## `whenLanguageMissing` (type: `string`):

What to do when a video has none of your languages: return the video's original captions (with a note), translate the original captions into your first language with YouTube's translation, or skip the transcript (delivered with reason "language-unavailable", not charged).

## `translateTo` (type: `string`):

Optional. Language code to translate every transcript into with YouTube's own translation, e.g. en, es, fr. Leave empty for no translation.

## `outputFormat` (type: `string`):

Items: one rich JSON record per video with the transcript, chapters and comments (best for API, code and AI). Rows: one flat row per transcript segment (or chunk) and per comment with the same columns everywhere (best for CSV, Excel, Google Sheets).

## `chunkSeconds` (type: `integer`):

For RAG / LLM pipelines: also split the transcript into chunks of up to this many seconds (transcript.chunks, each with start/end time, a link to that moment and its chapter). Chunks never cross a chapter. 0 = off. Can be combined with "Chunk size (words)".

## `chunkWords` (type: `integer`):

Split the transcript into chunks of up to this many words (e.g. 300). 0 = off. When both chunk settings are set, a chunk ends at whichever limit comes first.

## `leanOutput` (type: `boolean`):

Items format only: leave out transcript.srt, transcript.vtt and transcript.segments so each record stays small. Plain text, paragraphs, chunks, chapters and metadata stay.

## `includeComments` (type: `boolean`):

Also collect comments (author, text, likes, date, pinned / hearted / creator flags). Charged per comment delivered. Free plan: 50 per video.

## `maxCommentsPerVideo` (type: `integer`):

Upper limit of comments (replies included) per video. Free plan: capped at 50.

## `commentSort` (type: `string`):

Top comments (YouTube's ranking) or newest first.

## `includeReplies` (type: `boolean`):

Also collect replies to comments. Replies count toward "Max comments per video" and are charged like comments.

## `publishedAfter` (type: `string`):

Only videos from channels, playlists and searches published after this date or within this age: a date like 2026-09-01, or an age like "7 days", "24 hours", "2 weeks". Videos you paste directly are always processed. Older videos are left out and not charged.

## `includeShorts` (type: `boolean`):

Channels: also take videos from the Shorts tab. Searches: include Shorts in the results. Off: channels use the Videos tab and searches ask YouTube for regular videos only.

## `includeLiveStreams` (type: `boolean`):

Channels: also take finished live streams from the Live tab. Streams that are live right now or scheduled have no transcript yet and are always left out. Searches include finished streams anyway.

## `skipAlreadyScraped` (type: `boolean`):

Remember the IDs of videos whose transcript was delivered (in a named key-value store in your Apify account) and skip them in later runs, so scheduled runs only pay for new uploads. Videos without a transcript yet are tried again next time.

## `skipAlreadyScrapedStore` (type: `string`):

Name of the key-value store that keeps the already-scraped video IDs. Use a different name per schedule to keep separate lists; delete the store to start over. Letters, digits and hyphens.

## `proxyConfiguration` (type: `object`):

YouTube blocks repeated anonymous requests from one IP. Apify Proxy is recommended; sessions and IPs rotate automatically.

## `useResidentialFallback` (type: `boolean`):

If YouTube keeps blocking the default proxy, switch to Apify residential proxy automatically. No extra cost to you.

## `maxConcurrency` (type: `integer`):

How many videos to process at the same time, each with its own session (an IP that YouTube accepts is shared by at most two). Higher is faster for big lists.

## `maxRetries` (type: `integer`):

How many times to retry with a fresh session and IP when YouTube blocks or throttles a request.

## `maxSecondsPerVideo` (type: `integer`):

Stop retrying a video after this long so one stubborn video can't stall the run. The video is reported as failed and not charged.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=iG9CE55wbtY"
  ],
  "maxVideosPerSearch": 20,
  "searchSort": "relevance",
  "searchUploadDate": "any",
  "searchSubtitlesOnly": false,
  "maxVideosPerSource": 50,
  "includeTranscript": true,
  "whenLanguageMissing": "original",
  "outputFormat": "items",
  "chunkSeconds": 0,
  "chunkWords": 0,
  "leanOutput": false,
  "includeComments": false,
  "maxCommentsPerVideo": 100,
  "commentSort": "top",
  "includeReplies": false,
  "includeShorts": false,
  "includeLiveStreams": false,
  "skipAlreadyScraped": false,
  "skipAlreadyScrapedStore": "youtube-transcripts-seen",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "useResidentialFallback": true,
  "maxConcurrency": 4,
  "maxRetries": 8,
  "maxSecondsPerVideo": 90
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `runSummary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "startUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=iG9CE55wbtY"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("aaronoptimus/youtube-transcripts-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "startUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
        "https://www.youtube.com/watch?v=iG9CE55wbtY",
    ],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("aaronoptimus/youtube-transcripts-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=iG9CE55wbtY"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call aaronoptimus/youtube-transcripts-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,aaronoptimus/youtube-transcripts-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/PXaGkKkgrJMVaMDwb/builds/eZwDecGKF80x4osZq/openapi.json
