# YouTube Transcripts - Captions, SRT, Timestamps (`kaz_kakyo/youtube-transcripts`) Actor

Extract YouTube video transcripts via API, MCP, or schedule — captions (manual or auto-generated), timestamps, SRT subtitles, multi-language. $0.002 per video. One JSON row per URL. No browser, no API key.

- **URL**: https://apify.com/kaz\_kakyo/youtube-transcripts.md
- **Developed by:** [Heim AI](https://apify.com/kaz_kakyo) (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 $5.00 / 1,000 transcript fetcheds

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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 Transcripts — Captions from Video URLs

**URL in → transcript out.** Pass YouTube watch/shorts/youtu.be links; get one JSON dataset row per video with the caption track (manual or auto-generated). No API key, no browser. Built for **MCP agents, API clients, and scheduled pipelines**.

| | |
|---|---|
| **Actor id** | `kaz_kakyo/youtube-transcripts` |
| **Minimal input** | `{ "youtubeUrls": ["https://www.youtube.com/watch?v=…"] }` |
| **Cost** | **$0.005 per video** · $0.00005/run start |
| **Output** | Dataset rows with `type: "transcript"` or `type: "error"` |

### Call it (MCP / API / schedule)

#### MCP (agents)

```json
{
  "actor": "kaz_kakyo/youtube-transcripts",
  "input": {
    "youtubeUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"]
  }
}
```

Optional extras agents usually want:

```json
{
  "youtubeUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
  "languages": ["en"],
  "includeTimestamps": true,
  "includeSrt": true
}
```

After the run, read the default dataset. Every row has a `type` discriminator — filter on `"transcript"`; treat `"error"` as per-video failure. Missing captions, private/age-restricted/region-blocked videos, and bad URLs become error rows and the run still **SUCCEEDS** (including all-failed batches) so agent mistakes do not look like platform outages. The run fails only on a post-charge delivery failure (charged but could not write the dataset).

#### API / `apify-client`

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('kaz_kakyo/youtube-transcripts').call(
  { youtubeUrls: ['https://www.youtube.com/watch?v=jNQXAC9IVRw'], includeTimestamps: true },
  { maxTotalChargeUsd: 1.0 }, // hard budget for this run
);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
const transcripts = items.filter((i) => i.type === 'transcript');
```

Same shape via REST: `POST /v2/acts/kaz_kakyo~youtube-transcripts/runs` with your token, then poll or attach a webhook.

#### Make it recurring (what sticky callers do)

1. **Save a Task** in Console with your fixed options (`languages`, `includeTimestamps`, `includeSrt`). Agents and cron jobs call the **task id**, not ad-hoc input.
2. **Schedule the Task** (hourly/daily) when the URL list is stable — e.g. a channel watchlist you refresh elsewhere.
3. **Webhook on `SUCCEEDED`** to your endpoint / Zapier / Make — pull `defaultDatasetId` and process only `type === "transcript"` rows.
4. **Cap spend** on every automated run with `maxTotalChargeUsd`. When the cap hits, remaining videos become `type: "error"` skipped rows — no surprise bill, no silent free transcripts.
5. **Chain**: any YouTube search/channel scraper that outputs video URLs → this actor. For speech-to-text of *arbitrary* media files (not YouTube captions), use [`kaz_kakyo/audio-transcriber`](https://apify.com/kaz_kakyo/audio-transcriber) with a direct file URL instead.

Long runs checkpoint finished video IDs — a platform migration resumes without re-billing completed videos.

### Output contract

One dataset item per input URL (plus skipped/invalid rows). Success shape:

```json
{
  "type": "transcript",
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "videoId": "jNQXAC9IVRw",
  "title": "Me at the zoo",
  "channel": "jawed",
  "channelId": "UC…",
  "durationSeconds": 19,
  "language": "en",
  "languageName": "English",
  "autoGenerated": false,
  "languageFallback": false,
  "transcript": "Full caption text…",
  "availableLanguages": [{ "language": "en", "name": "English", "autoGenerated": false }],
  "segments": [{ "start": 0.0, "duration": 2.1, "text": "…" }],
  "srt": "1\n00:00:00,000 --> …"
}
```

| Field | When present |
|---|---|
| `transcript`, `videoId`, `title`, `channel`, `durationSeconds`, `language`, `autoGenerated` | always on success |
| `languageFallback` | true when preferred `languages` did not match and another track was used |
| `availableLanguages` | always on success |
| `segments` | `includeTimestamps: true` |
| `srt` | `includeSrt: true` |
| `segmentsUrl` / `srtUrl` | rare — oversized payloads spilled to the key-value store |

Failure / skip row (never charged):

```json
{ "type": "error", "url": "https://…", "videoId": "…", "error": "…", "errorCode": "captions_disabled" }
```

Download the dataset as JSON, CSV, Excel, or HTML from Console or the dataset API.

### Why this one

- **Cheap captions path.** $0.005 per video undercuts typical Store $0.01+/result listings. Near-zero compute — HTTP only, no browser, no Whisper.
- **Agent-safe.** Bad/missing-caption URLs become `type: "error"` rows; the run still succeeds.
- **Timestamps + SRT** when you need them; plain `transcript` by default for RAG / MCP context packing.
- **Language-aware.** Prefer your `languages` list; manual captions beat auto-generated within a language.

### Pricing

| Event | Price | When |
|---|---|---|
| Transcript fetched | **$0.005** | Successful caption delivery (one charge per video) |
| Actor start | $0.00005 | Per run |

Error / skipped rows are **never** billed. Cap spend with `maxTotalChargeUsd` on the run or task.

### Input rules agents must follow

- **`youtubeUrls` (required)** — YouTube `watch` / `shorts` / `youtu.be` / `embed` / `live` URLs, or bare 11-char video IDs. Max 500 per run. Duplicates are collapsed by video ID.
- **`languages`** — optional preferred BCP-47 codes in order (default prefers `en`, then any available).
- **`includeTimestamps`** — adds `segments[]` with `{ start, duration, text }`.
- **`includeSrt`** — adds an `.srt`-format string.

Videos with captions disabled, private/deleted, age-restricted, or region-blocked return error rows. This actor fetches **existing YouTube captions** — it does not run speech-to-text. For arbitrary audio/video file URLs, use `kaz_kakyo/audio-transcriber`.

See the **Input** tab for the full schema. See the **API** tab for run/dataset endpoints.

### FAQ

**Do I need a YouTube API key?** No. The actor uses the public Innertube player + timedtext endpoints.

**Manual vs auto captions?** Within a preferred language, manual tracks are chosen over auto-generated (`autoGenerated: true` when ASR was used).

**Why did my URL fail?** No public captions, or the video is private / age-gated / region-blocked. Check `error` / `errorCode` on the dataset row — the run status will still be SUCCEEDED.

**How do I keep costs predictable on a schedule?** Set `maxTotalChargeUsd` on the run/task.

***

*If this saved you time, a Store review on the [actor page](https://apify.com/kaz_kakyo/youtube-transcripts) helps a solo dev. Hit a problem? [Open an issue](https://apify.com/kaz_kakyo/youtube-transcripts/issues).*

### Telemetry

Each run records one anonymous event: a salted hash of the caller account ID (never the raw ID), run origin (Console / API / MCP / …), and a timestamp. No inputs, results, or personal data — adoption measurement only.

# Actor input Schema

## `youtubeUrls` (type: `array`):

Required. YouTube watch / shorts / youtu.be / embed / live URLs, or bare 11-char video IDs. One dataset row per URL; max 500 per run. Minimal call: only this field. Videos without captions, private/age-restricted/region-blocked links become type:error rows; the run still succeeds.

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

Optional BCP-47 codes tried in order (e.g. `en`, `es`, `de`). Within a language, manual captions beat auto-generated. If none match, falls back to the best available track and sets `languageFallback: true`. Empty = prefer English, then any available.

## `includeTimestamps` (type: `boolean`):

Default: false. When true, adds `segments[]`: `{ start, duration, text }` per caption cue. Useful for programmatic indexing and RAG chunking.

## `includeSrt` (type: `boolean`):

Default: false. When true, adds `srt` (ready-to-save `.srt` string from the caption track).

## Actor input object example

```json
{
  "youtubeUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "languages": [
    "en"
  ],
  "includeTimestamps": false,
  "includeSrt": false
}
```

# Actor output Schema

## `overview` (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 = {
    "youtubeUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    ],
    "languages": [
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("kaz_kakyo/youtube-transcripts").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 = {
    "youtubeUrls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
    "languages": ["en"],
}

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

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

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

```

## CLI example

```bash
echo '{
  "youtubeUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "languages": [
    "en"
  ]
}' |
apify call kaz_kakyo/youtube-transcripts --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=kaz_kakyo/youtube-transcripts",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/acts/BmdWUcQN54p3NdBgz/builds/g7NEjLT9hdT6C5C0a/openapi.json
