# YouTube Transcript (`scrapewise/youtube-transcript`) Actor

Extract YouTube transcripts and subtitles from videos, channels, playlists or search as text, timestamps, SRT or VTT. Adds publish date, views, length, description and thumbnail. Only-new-videos mode for schedules. Videos without captions are free and never fail the run. Pay per transcript.

- **URL**: https://apify.com/scrapewise/youtube-transcript.md
- **Developed by:** [Scrapewise Data](https://apify.com/scrapewise) (community)
- **Categories:** Videos, AI, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.50 / 1,000 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YouTube Transcript

Get the full transcript of any YouTube video, channel, playlist or search as clean text, timestamped segments, SRT or VTT, with the video's publish date, views, length, description and thumbnail. Ready for AI summaries, RAG, n8n workflows, subtitles or content research.

### Why this one

- **Videos, channels, playlists and searches in one field.** Paste a channel (`/@handle`), a playlist or a YouTube search URL and get the transcripts of its videos, with a limit per source, a "published after" date and Shorts on or off.
- **Only new videos since the last run.** Turn on `onlyNewVideos`, schedule the Actor daily and each run transcribes only the videos you did not get yet.
- **Videos without captions never fail your run.** They come back as an item with `errorCode` (for example `NO_CAPTIONS`) and are **not charged**, so a single-video call from n8n, Make or Zapier does not break your workflow.
- **Checks twice before saying "no captions".** Captions are read straight from YouTube's player with two different app clients, and a second method is tried before a video is reported as having no captions.
- **Video details included:** `publishedAt`, `viewCount`, `lengthSeconds`, `description`, `thumbnailUrl`, `channelId`, title and channel, at no extra cost.
- **Pick the captions you want:** preferred languages in order, manual or auto-generated only, and optional translation.
- **SRT and VTT included** at the same price, next to the plain text and segments.
- **Drop-in replacement:** accepts the input field names of other transcript scrapers (`videoUrl`, `urls`, `startUrls`, `youtube_url`, `targetLanguage`), so you can switch without changing your integration.

### Use cases

- Feed video transcripts to ChatGPT, Claude or your RAG pipeline to summarize or answer questions about videos.
- Monitor a channel: a daily schedule with `onlyNewVideos` sends each new video's transcript to your workflow.
- Turn a course playlist, a podcast channel or the top results of a search into searchable text for SEO, notes or blog posts.
- Download subtitles as SRT or VTT for editing or re-uploading.

### Input

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://www.youtube.com/@veritasium",
    "https://www.youtube.com/playlist?list=PL8dPuuaLjXtNlUrzyH5r6jN9ulIgZBpdo",
    "https://www.youtube.com/results?search_query=rust+tutorial"
  ],
  "searchQueries": ["receita de bolo"],
  "maxVideosPerSource": 10,
  "publishedAfter": "2026-01-01",
  "includeShorts": false,
  "onlyNewVideos": false,
  "languages": ["en", "es"],
  "captionType": "any",
  "includeSegments": true,
  "subtitleFormats": ["srt"],
  "maxItems": 50
}
```

- **Videos:** watch, youtu.be, shorts, embed and live URLs, or plain 11-character IDs. Duplicates are removed.
- **Channels:** `/@handle`, `/channel/UC...`, `/c/name` and `/user/name`, listed newest first. With `includeShorts` off you get regular videos and past live streams; with it on, Shorts too.
- **Playlists:** any URL with `list=` that is not a single watch URL, or a bare playlist ID. Videos come in playlist order.
- **Searches:** YouTube search URLs (`/results?search_query=...`, filters in the URL are kept) or plain terms in `searchQueries`. Only videos are taken.
- `maxVideosPerSource` limits each channel, playlist or search; `maxItems` limits the whole run.
- Videos are processed in parallel, so items land in the dataset in the order they finish; sort by `publishedAt` if you need a timeline.
- `publishedAfter` skips older videos for every input; skipped videos produce no item and cost nothing.
- `onlyNewVideos` stores the IDs of delivered transcripts per channel, playlist or search in a named key-value store in your account (`youtube-transcript-only-new-videos`). Videos that had no captions are tried again on the next run, free.

**Switching from another transcript scraper?** These work as is: `{"videoUrl": "https://www.youtube.com/watch?v=...", "targetLanguage": "en"}`, `{"urls": ["..."]}`, `{"startUrls": [{"url": "..."}]}`, `{"youtube_url": "..."}`.

### Output

One item per video:

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channel": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "publishedAt": "2009-10-25T06:57:33+00:00",
  "viewCount": 1815077270,
  "lengthSeconds": 213,
  "thumbnailUrl": "https://i.ytimg.com/vi_webp/dQw4w9WgXcQ/sddefault.webp",
  "description": "The official video for “Never Gonna Give You Up” by Rick Astley ...",
  "isLiveContent": false,
  "category": "Music",
  "language": "en",
  "languageName": "English",
  "isAutoGenerated": false,
  "availableLanguages": ["en", "de-DE", "ja", "pt-BR", "es-419", "en"],
  "text": "[♪♪♪] ♪ We're no strangers to love ♪ ...",
  "wordCount": 487,
  "durationSeconds": 211.3,
  "segments": [{ "text": "[♪♪♪]", "start": 1.36, "duration": 1.68 }],
  "srt": "1\n00:00:01,360 --> 00:00:03,040\n[♪♪♪]\n...",
  "error": null,
  "errorCode": null,
  "sourceUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "scrapedAt": "2026-09-13T01:30:00+00:00"
}
```

- `lengthSeconds` is the real length of the video; `durationSeconds` is where the last caption ends.
- `sourceUrl` is the input the video came from (the video itself, or the channel, playlist or search).

Items that could not be transcribed have `error` and `errorCode` filled in (and the video details when YouTube returned them) and **are not charged**:

| errorCode | Meaning |
|---|---|
| `NO_CAPTIONS` | The video has no captions |
| `NO_CAPTIONS_IN_LANGUAGE` | No captions of the requested caption type |
| `VIDEO_UNAVAILABLE` / `VIDEO_UNPLAYABLE` | Private, removed or region-locked video |
| `AGE_RESTRICTED` | Age restricted video |
| `LIVE_NOT_ENDED` | Live stream still running, upcoming, or its recording is not available |
| `INVALID_VIDEO_ID` | Not a valid video |
| `BLOCKED` | YouTube blocked every attempt; try again |

The run itself only fails when nothing was delivered and YouTube blocked the requests, or when no channel, playlist or search could be listed.

### Pricing

**From US$ 3.50 per 1,000 transcripts delivered, no monthly fee.** US$ 5.00 on the Free plan, lower on paid Apify plans:

| Apify plan | Per 1,000 transcripts |
|---|---|
| Free | US$ 5.00 |
| Starter (Bronze) | US$ 4.50 |
| Scale (Silver) | US$ 4.00 |
| Business (Gold) and above | US$ 3.50 |

One charge = one video whose transcript was delivered, whatever the length or format. Listing channels, playlists and searches is free, and so are videos with captions disabled, unavailable or blocked.

### How to use

- **Console:** paste the URLs, click Start, download JSON, CSV or Excel.
- **API:** `POST https://api.apify.com/v2/acts/scrapewise~youtube-transcript/run-sync-get-dataset-items?token=YOUR_TOKEN` with the input above as the body.
- **Schedule:** run daily over your channels with `onlyNewVideos` on, using Apify Schedules.
- **Integrations:** n8n, Make, Zapier and Google Sheets through the Apify integrations.

### FAQ

**Do I need to configure anything?** No. Paste video, channel, playlist or search URLs and click Start. Everything else has working defaults.

**Am I charged for videos without captions?** No. You are charged once per video whose transcript was delivered. Failed videos, listing channels and playlists, and skipped videos are free.

**What does "per 1,000 transcripts" mean?** Each delivered transcript is one charge, whatever the video length or output format. 1,000 videos with transcripts = the price shown.

**Can I get transcripts in a specific language?** Yes. Put language codes in `languages` in order of preference (for example `["es", "en"]`). If none exists, the first manual caption is used, then the auto-generated one.

**Can I get only the videos a channel published since my last run?** Yes. Turn on `onlyNewVideos` and schedule the Actor. Each run delivers only new videos.

**How do I use it from n8n, Make or Zapier?** Use the Apify integration and call `scrapewise/youtube-transcript` with the input above. One video per call works well, and a video without captions returns an item with `errorCode` instead of failing the workflow.

**Can I use it from an AI agent (MCP)?** Yes, through the Apify MCP server, like any Apify Actor.

**It said NO\_CAPTIONS but the video has captions.** Open an issue with the video URL. We check two YouTube clients and a second method before reporting no captions, and we fix reported cases fast.

**Does it transcribe audio?** No. It reads the captions YouTube already has (manual or auto-generated), which covers most videos.

### Changelog

- **2026-09-14:** lower price, from US$ 3.50 per 1,000 on paid plans (US$ 5.00 on Free).
- **2026-09-13:** shorter title, simpler input (advanced options grouped), FAQ.
- **2026-09-12:** channels, playlists and searches; only-new-videos mode; publish date, views, length, description and thumbnail; captions read straight from YouTube's player (about 3x cheaper to run, same success rate); `LIVE_NOT_ENDED` error code.
- **2026-09-12:** videos without captions no longer fail the run; stable `errorCode`; SRT and VTT output; caption type; accepts input field names of other transcript scrapers.
- **2026-09-10:** first release.

### Limitations

- Only videos that have captions (manual or auto-generated) can be transcribed. This Actor does not transcribe audio.
- Translation uses YouTube's own translation, and YouTube refuses most of these requests. When it does, the original language is delivered and the reason is recorded in `translationError`.
- Age-restricted videos and live streams that are still running cannot be transcribed.
- `publishedAt` comes from a separate lightweight request; in rare cases it can be empty while the transcript is delivered.
- YouTube blocks most datacenter IPs, so residential proxy is the default. Keep it on.
- Something broke? Open an issue on the Actor page. Issues are answered within 12 hours.

This Actor collects only public data and respects the site's terms.

Keywords: youtube transcript, youtube transcripts, youtube transcript scraper, youtube to text, youtube transcript api, youtube channel transcripts, youtube playlist transcript, download youtube subtitles, youtube srt.

# Actor input Schema

## `videoUrls` (type: `array`):

Mix freely: video URLs (watch, youtu.be, shorts, embed, live) or 11-character IDs; channel URLs (/@handle, /channel/UC..., /c/..., /user/...); playlist URLs (list=...); YouTube search URLs (/results?search\_query=...). Channels, playlists and searches are expanded into videos, limited by 'Max videos per channel, playlist or search'.

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

Search YouTube for these terms and transcribe the videos found (videos only, no channels or playlists), limited by 'Max videos per channel, playlist or search'.

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

Language codes in order of preference (e.g. en, pt, pt-BR, es). If none is available, the first manual caption is used, then the auto-generated one.

## `maxItems` (type: `integer`):

Hard limit for the whole run, across all inputs. Protects your budget.

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

How many videos to take from each channel, playlist or search. Channels are listed newest first.

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

Skip videos published before this date (YYYY-MM-DD). Applies to every input, including single videos. Skipped videos produce no item and are not charged.

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

Include Shorts when listing a channel or a search. Off: a channel gives its regular videos and past live streams.

## `onlyNewVideos` (type: `boolean`):

Remembers which videos of each channel, playlist or search already got a transcript (in a named key-value store in your account, 'youtube-transcript-only-new-videos') and skips them on the next runs. Ideal for a daily or weekly schedule. Videos without captions are tried again next time, free.

## `captionType` (type: `string`):

Which captions to use. Videos without the requested type come back with errorCode NO\_CAPTIONS\_IN\_LANGUAGE and are not charged.

## `includeSegments` (type: `boolean`):

Adds a 'segments' array with text, start and duration (seconds) for each caption line.

## `subtitleFormats` (type: `array`):

Adds ready-to-use 'srt' and/or 'vtt' fields with the full subtitle file. Same price.

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

Language code to translate the transcript with YouTube's own translation (e.g. pt, en). YouTube refuses most of these requests; when it does, the original language is delivered and the reason is recorded in translationError.

## `videoUrl` (type: `string`):

One video URL. Same field name as other transcript scrapers, so you can switch without changing your integration. Merged with the list above.

## `targetLanguage` (type: `string`):

Language code of the transcript to fetch first (e.g. en, pt). Same field name as other transcript scrapers; it goes to the top of 'Preferred languages'. For translation use 'Translate to'.

## `tryDatacenterFirst` (type: `boolean`):

Each video is tried twice through datacenter proxy before falling back to the proxy below. Keeps runs fast and cheap; turn off only if you want residential from the first request.

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

YouTube blocks most datacenter IPs; residential proxy is the default and recommended.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "languages": [
    "en",
    "pt",
    "pt-BR",
    "es"
  ],
  "maxItems": 50,
  "maxVideosPerSource": 10,
  "includeShorts": false,
  "onlyNewVideos": false,
  "captionType": "any",
  "includeSegments": true,
  "subtitleFormats": [],
  "tryDatacenterFirst": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

No description

## `resultsCsv` (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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapewise/youtube-transcript").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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    ],
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapewise/youtube-transcript").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 '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call scrapewise/youtube-transcript --silent --output-dataset

```

## MCP server setup

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

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/saNmHpGKA0ZHoUOId/builds/BAc8jzLDRXS2sNhjj/openapi.json
