# YouTube Transcript Scraper – Clean Text & JSON (`chronometrica/youtube-transcript-scraper`) Actor

Extract clean, timestamped transcripts from public YouTube videos and Shorts. Batch up to 1,000 URLs, choose caption languages, and export text or structured segments for AI, research, SEO, and automation. Pay only for successful transcripts.

- **URL**: https://apify.com/chronometrica/youtube-transcript-scraper.md
- **Developed by:** [Chronometrica](https://apify.com/chronometrica) (community)
- **Categories:** Social media, Videos, AI
- **Features:** Limited permissions
- **Stats:** 5 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 youtube transcript results

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

### 🚀 What does YouTube Transcript Scraper do?

Paste YouTube video URLs. Get clean transcript text and timestamped segments
that are ready for AI, research, spreadsheets, and automated workflows.

The Actor extracts existing public captions from videos and Shorts. It also
returns video context, selected language details, and clear result states. No
YouTube API key, account, or cookies are required from you.

### 🎯 What can you use it for?

- 🤖 Send clean transcript text into ChatGPT, Claude, AI agents, RAG pipelines,
  embeddings, and LLM prompts.
- ✂️ Pull out key points, summaries, quotes, clips, and content ideas without
  watching every video first.
- 🔎 SEO, competitive content analysis, media monitoring, and creator research.
- 🎓 Academic, market, podcast, lecture, interview, and training-data workflows.
- ♿ Accessibility review and subtitle/caption export.
- 📊 Load stable rows into warehouses, Google Sheets, no-code automations, and
  repeatable batch jobs.

The Actor is built for direct video objects. Each successful YouTube video or
Short becomes one billable transcript row. Failed inputs stay non-billable and
are counted in the run `OUTPUT` record.

### ✨ Why use this Actor?

- ✅ **Clean output without formatting fluff**: timestamped `segments` plus one
  joined `text` field.
- 🔗 **Easy to integrate**: use stable dataset rows with the Apify API, webhooks,
  schedules, Make, Zapier, n8n, or your own code.
- 📺 **Video and channel context**: title, publish date, duration, channel,
  and thumbnail when YouTube exposes them.
- 📈 **Transcript quality signals**: word count, character count, segment count,
  timing coverage, and coverage ratio.
- 🌍 **Language clarity**: requested languages, selected language, available
  language codes, auto-generated/manual flag, and match reason.
- 🩳 **Shorts and long videos**: works with normal videos, Shorts, and long
  public videos when captions are available.
- 🔑 **No API key required from you**: use direct video URLs, Shorts URLs,
  `youtu.be`, embed URLs, or raw video IDs.
- 💸 **Non-billable failures**: invalid, unsupported, unavailable, age-restricted,
  login-required, and no-transcript outcomes are reported in `OUTPUT`, not
  written as charged dataset rows.
- 🧾 **Clear billing unit**: one successful transcript equals one dataset row and
  one billable event. A failed lookup costs no result event.
- 🆕 **Fresh on every run**: each public run fetches the current public caption
  track instead of reusing an older transcript result.

### ⚙️ Can I use this Actor through an API?

Yes. You can run YouTube Transcript Scraper manually in Apify Console or use it
through:

- Apify API
- Python SDK
- Node.js SDK
- Webhooks
- Scheduled runs
- Apify integrations

This makes it useful for AI pipelines, research tools, spreadsheets, data
warehouses, and no-code automations.

### 🚀 How do I use YouTube Transcript Scraper?

Paste one or more direct YouTube video URLs, Shorts URLs, `youtu.be` links,
embed URLs, or raw video IDs into `startUrls`.

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

That is enough for most jobs:

1. Open **YouTube Transcript Scraper** in Apify Console.
2. Paste one or more YouTube video URLs or IDs.
3. Leave the default language and output settings for your first run.
4. Click **Start**.
5. Open **Output** to inspect the transcript rows and run summary.
6. Download the dataset or send it to another tool through an integration.

### 🧩 Input

| Option                      | What it controls                                                                                               |
| --------------------------- | -------------------------------------------------------------------------------------------------------------- |
| `startUrls`                 | One to 1,000 direct video, Shorts, live, embed, `youtu.be`, or raw video ID inputs. Duplicate videos run once. |
| `preferredLanguages`        | Optional language codes in priority order. Empty means best available. A filled list is strict.                |
| `outputFormats`             | One or more of `json`, `text`, `srt`, `vtt`, and `llm`. Defaults to `json` and `text`.                         |
| `includeFailedInputDetails` | Include safe per-input failure details in `OUTPUT`. Defaults to `true`. Failures are never dataset rows.       |

When `preferredLanguages` is set and no requested language exists, the Actor
records a typed non-billable `LANGUAGE_UNAVAILABLE` failure in `OUTPUT`. It
does not silently return another language.

### 📦 Output

Each default-dataset row represents one successful unique YouTube video or
Short. The fields are ordered for clean loading into spreadsheets, APIs, and
warehouses.

| Group      | Fields                                                                                                                                            |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| Input      | `platform`, `inputIndex`, `inputUrl`                                                                                                              |
| Video      | `videoUrl`, `videoId`, `title`, `publishedAt`, `videoDurationSeconds`, `thumbnailUrl`                                                             |
| Channel    | `channelId`, `channelName`, `channelUrl`                                                                                                          |
| Language   | `requestedLanguageCodes`, `transcriptLanguageCode`, `transcriptLanguageName`, `availableLanguageCodes`, `languageMatch`, `isAutoGenerated`        |
| Transcript | `segments`, `text`, optional `srt`, optional `vtt`, optional `llmText`                                                                            |
| Quality    | `segmentCount`, `wordCount`, `characterCount`, `firstSegmentStartSeconds`, `lastSegmentEndSeconds`, `transcriptSpanSeconds`, `videoCoverageRatio` |
| Result     | `status`, `statusReason`, `errorCategory`, `errorCode`, `isRetryable`, `scrapedAt`                                                                |

#### ✅ Successful row sample

```json
{
    "platform": "youtube",
    "inputIndex": 0,
    "inputUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
    "videoId": "dQw4w9WgXcQ",
    "title": "Example video title",
    "publishedAt": "2024-04-30",
    "videoDurationSeconds": 212,
    "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg",
    "channelId": "UCexample123",
    "channelName": "Example Channel",
    "channelUrl": "https://www.youtube.com/@example",
    "requestedLanguageCodes": ["en"],
    "transcriptLanguageCode": "en",
    "transcriptLanguageName": "English",
    "availableLanguageCodes": ["en", "es"],
    "languageMatch": "exact",
    "isAutoGenerated": false,
    "segments": [
        {
            "startSeconds": 0,
            "endSeconds": 2.4,
            "durationSeconds": 2.4,
            "text": "Example transcript text"
        }
    ],
    "text": "Example transcript text",
    "srt": null,
    "vtt": null,
    "llmText": null,
    "segmentCount": 61,
    "wordCount": 534,
    "characterCount": 3180,
    "firstSegmentStartSeconds": 0,
    "lastSegmentEndSeconds": 210.4,
    "transcriptSpanSeconds": 210.4,
    "videoCoverageRatio": 0.9925,
    "status": "succeeded",
    "statusReason": "Transcript parsed from public YouTube caption tracks.",
    "errorCategory": null,
    "errorCode": null,
    "isRetryable": false,
    "scrapedAt": "2026-07-08T21:43:38.000Z"
}
```

#### ⚠️ Failed input detail

When enabled, `OUTPUT.failedInputDetails` preserves safe input and canonical
identity details without creating a billable dataset row.

```json
{
    "inputIndex": 1,
    "inputUrl": "https://www.youtube.com/watch?v=example1234",
    "videoUrl": "https://www.youtube.com/watch?v=example1234",
    "videoId": "example1234",
    "status": "failed",
    "statusReason": "No public transcript was available.",
    "errorCategory": "no_transcript",
    "errorCode": "TRANSCRIPT_NOT_FOUND",
    "isRetryable": false
}
```

Common failure codes:

- `TRANSCRIPT_NOT_FOUND`: public video resolved, but no public caption track was available.
- `VIDEO_UNAVAILABLE`: the video was unavailable.
- `AGE_RESTRICTED`: YouTube required age-confirmed access.
- `LOGIN_REQUIRED`: YouTube required login for the lookup.
- `LANGUAGE_UNAVAILABLE`: none of the requested transcript languages were available.
- `INVALID_INPUT` and `UNSUPPORTED_INPUT`: the input was not a supported direct video object.
- `ACCESS_BLOCKED`, `TEMPORARY_FAILURE`, and `TRANSCRIPT_PARSE_FAILED`: the public lookup could not produce a transcript and may succeed later when `isRetryable` is true.

### 🧾 Run summary

The `OUTPUT` record shows:

- inputs received;
- unique videos processed;
- successful transcript rows;
- duplicate videos skipped;
- unavailable or unsupported inputs;
- error-code counts;
- optional safe details for inputs that did not produce a transcript.

Use this summary to check the whole run before loading the dataset.

### 💵 Pricing Event

The Actor is designed for Pay Per Event pricing. One billable event is one
successful transcript row written to the dataset. Failed, invalid, unsupported,
age-restricted, unavailable, language-unavailable, and no-transcript rows are
non-billable. Public runs always fetch a fresh caption result.

### 🚫 What this Actor is not

- It does not generate AI speech-to-text when YouTube has no public captions.
- It does not download video or audio files.
- It does not use your YouTube account, cookies, OAuth, or private session.
- It does not access private, deleted, members-only, or account-only videos.
- It does not expand playlists, channels, search keywords, recommendations, or
  comments. Use a discovery Actor first, then pass direct video URLs here.
- It does not guarantee every public video has captions. When captions are
  unavailable, the row explains the outcome and stays non-billable.

### 🏆 Best practices

- 🎯 Pass direct video or Shorts URLs whenever possible.
- 🧠 Keep `outputFormats` to `["json", "text"]` for most AI and data workflows.
- 📝 Add `srt` or `vtt` only when you need subtitle files.
- 🌐 Leave `preferredLanguages` empty for the best available track. Add language
  codes in priority order when the result must use one of those languages.
- 🔗 For channel, playlist, or keyword workflows, run discovery first and send the
  resulting direct video URLs into this Actor.

### 🔗 Related social media scrapers

Continue the same workflow on this platform or compare public data across
platforms.

#### Continue on YouTube

- [YouTube Channel Scraper](https://apify.com/chronometrica/youtube-channel-scraper)
  — collect public channel details, links, and audience stats.
- [YouTube Search Scraper](https://apify.com/chronometrica/youtube-search-scraper)
  — find public videos, Shorts, channels, and metrics by keyword.
- [YouTube Metrics Scraper](https://apify.com/chronometrica/youtube-metrics-scraper)
  — collect public video, Short, channel, and engagement metrics.
- [YouTube Comments Scraper](https://apify.com/chronometrica/youtube-comments-scraper)
  — collect public comments, replies, authors, likes, pins, and creator
  hearts.

#### Compare transcripts across platforms

- [TikTok Transcript Scraper](https://apify.com/chronometrica/tiktok-transcript-scraper)
  — extract public caption text and timestamped segments.

# Changelog

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

# Actor input Schema

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

Paste direct video, Shorts, live, embed, or youtu.be URLs, or 11-character video IDs. Duplicate video IDs are processed once. Invalid non-empty inputs are reported in OUTPUT without creating billable dataset rows.

## `preferredLanguages` (type: `array`):

Leave empty to use the best available caption track. When set, languages are tried in order. If none match, the Actor reports a typed non-billable language-unavailable failure instead of another language.

## `outputFormats` (type: `array`):

Choose one or more transcript renderings. JSON gives timestamped segments. Text gives clean plain text. SRT and VTT are subtitle formats. LLM gives prompt-ready text.

## `includeFailedInputDetails` (type: `boolean`):

Add safe per-input failure details to the run OUTPUT record. Failed inputs never become billable dataset rows.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "preferredLanguages": [],
  "outputFormats": [
    "json",
    "text"
  ],
  "includeFailedInputDetails": true
}
```

# Actor output Schema

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

No description

## `output` (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"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("chronometrica/youtube-transcript-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"] }

# Run the Actor and wait for it to finish
run = client.actor("chronometrica/youtube-transcript-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"
  ]
}' |
apify call chronometrica/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,chronometrica/youtube-transcript-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/Z3oBut4UdVlAAsORu/builds/BIEUgPg9mnee8138o/openapi.json
