YouTube transcript agent skill for Claude Code and agents

This skill retrieves YouTube transcripts, timed segments and video metadata through an Apify Actor, with optional Whisper transcription when captions are unavailable. Choose it for transcript extraction, subtitle preparation or collecting spoken content from public videos, channels and playlists.

Folder on GitHub Run on Apify

The YouTube Caption & Whisper AI Transcript Extractor Actor combines caption retrieval with an optional audio transcription fallback. The skill supplies instructions, schema references, an example input and a runner for requesting transcript data in the format the agent needs.

Install

  1. Copy the folder youtube-caption-and-whisper-ai-transcript-extractor from GitHub, or save the three files below into it.

  2. Place it in ~/.claude/skills/youtube-caption-and-whisper-ai-transcript-extractor/ for Claude Code, or ~/.agents/skills/youtube-caption-and-whisper-ai-transcript-extractor/ for Codex and other skill-aware agents.

  3. Export APIFY_TOKEN, start the agent and ask for the job in plain words; the description tells the agent when to use the skill.

  4. Or let the skills CLI place the Actor's SKILL.md for your agent: npx skills add https://recipeforagents.com --skill youtube-caption-and-whisper-ai-transcript (the SKILL.md only; the runner script comes from the GitHub folder).

SKILL.mdyoutube-caption-and-whisper-ai-transcript-extractor/SKILL.md

---
name: youtube-caption-and-whisper-ai-transcript-extractor
description: "Get timestamped transcripts from YouTube videos in your preferred language, with optional AI transcription for videos that have no captions at all. Returns full text, timed segments, and core video metadata. Output fields: video_id, title, channel_name, publishedAt, transcript_language and more, as JSON rows. Use it for requests like \"YouTube caption scraper\", or a CSV/JSON export of public YouTube data."
---

# YouTube Caption & Whisper AI Transcript Extractor

Get timestamped transcripts from YouTube videos in your preferred language, with optional AI transcription for videos that have no captions at all. Returns full text, timed segments, and core video metadata.

- Actor: https://apify.com/nimbryxo-studio/youtube-caption-and-whisper-ai-transcript (ID `nimbryxo-studio~youtube-caption-and-whisper-ai-transcript`)
- n8n workflow for the same Actor: https://github.com/bolvein-firex/scraping-n8n-workflows/blob/main/docs/youtube-caption-and-whisper-ai-transcript-extractor.md
- Example input: `references/example-input.json`
- Input schema: `references/input-schema.json`
- Output fields: `references/output-fields.json`

## When to use

Use this skill when the user wants:

- YouTube caption scraper
- a repeatable export of public YouTube data as CSV or JSON

Do not use it for private, login-protected or paywalled data.

## What it returns

One JSON row per result. Columns from the Actor's dataset schema:

- `video_id`: Video id
- `title`: Title
- `channel_name`: Channel name
- `publishedAt`: Published At
- `transcript_language`: Transcript language
- `segment_count`: Segment count
- `success`: Success
- `transcript_available`: Transcript available
- `transcript_source`: Transcript source
- `accessStatus`: Access Status
- `errorCode`: Error Code
- `duration_seconds`: Duration seconds

All 31 declared fields: `references/output-fields.json`.

## Inputs

| Field | Type | Required | Default | Notes |
|---|---|---|---|---|
| `mode` | string | no | videos | What to scrape: specific videos, a channel's uploads, or a playlist. |
| `videoUrls` | array | no |  | YouTube watch URLs, youtu.be short links, Shorts/embed URLs, or plain 11-character video IDs (mode=videos). Duplicate IDs are processed once. |
| `channelUrls` | array | no |  | Channel URLs or handles (mode=channel), e.g. `https://www.youtube.com/@TED,` `https://www.youtube.com/channel/UC...,` or @TED. |
| `maxVideosPerChannel` | integer | no | 50 | Maximum number of videos to enumerate from each channel (mode=channel). 1-1,000. |
| `playlistUrls` | array | no |  | Playlist URLs or IDs (mode=playlist), e.g. `https://www.youtube.com/playlist?list=PL...` or PL.... Every video in the playlist is transcribed. |
| `maxVideosPerPlaylist` | integer | no | 50 | Maximum number of videos to enumerate from each playlist (mode=playlist). 1-1,000. |
| `language` | string | no |  | Caption language to fetch. Leave Auto-detect to use the video's original language. |
| `includeAutoGenerated` | boolean | no | true | Whether to include auto-generated captions when manual captions are not available. |
| `useWhisper` | boolean | no | true | When YouTube has no transcript (disabled captions, music-only, or blocked), download the audio and transcribe with Whisper AI (faster-whisper, CPU). Adds ~30-180 s per video. |
| `whisperModel` | string | no | base | Whisper model size to use when `useWhisper` is enabled. Larger = more accurate but slower and more RAM. 'base' is balanced. |
| `includeWordTimings` | boolean | no | false | Add a wordTimings array with word-level start/end times. Only auto-generated YouTube captions expose word timings; manual captions and AI transcripts do not. |
| `maxWhisperMinutes` | integer | no |  | Leave unset for the legacy behavior (AI enabled with no length limit). Set to 0 explicitly to disable AI transcription. |
| `proxyCountry` | string | no | US | Country/region for the proxy IP. Useful if transcripts are geo-blocked in the default US region. |
| `outputFormat` | string | no | full | How transcript data is included. 'full' = segments and full_text; 'text_only' = full_text only; 'segments_only' = segments only; 'srt' / 'vtt' = ready-to-use subtitle text derived… |

Example (`references/example-input.json`):

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "whisperModel": "tiny",
  "proxyCountry": "GB"
}
```

The input passes the Actor's input schema (checked at every publish). First run: `maxVideosPerChannel` = 50, small enough for the 300-second sync wait; raise it once the rows look right.

## Run it

Set `APIFY_TOKEN`, then call the synchronous endpoint for small and medium runs (it waits up to 300 seconds):

```bash
curl -s -X POST "https://api.apify.com/v2/acts/nimbryxo-studio~youtube-caption-and-whisper-ai-transcript/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d @references/example-input.json > output.json
```

Or with Node 18+: `node scripts/run.mjs [input.json]`.

For large runs, start an async run and read its dataset when it finishes:

```bash
RUN=$(curl -s -X POST "https://api.apify.com/v2/acts/nimbryxo-studio~youtube-caption-and-whisper-ai-transcript/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" -H "Content-Type: application/json" \
  -d @references/example-input.json)
RUN_ID=$(echo "$RUN" | jq -r '.data.id'); DATASET=$(echo "$RUN" | jq -r '.data.defaultDatasetId')
curl -s "https://api.apify.com/v2/actor-runs/$RUN_ID?waitForFinish=60" -H "Authorization: Bearer $APIFY_TOKEN" | jq -r '.data.status'
curl -s "https://api.apify.com/v2/datasets/$DATASET/items?format=json" -H "Authorization: Bearer $APIFY_TOKEN" > output.json
```

## Cost and limits

- Apify bills the Actor's usage on your account; the Actor's Pricing tab shows the current price.
- Keep the limit in the input small for a first run.

## Guardrails

- Public data only. Do not pass cookies, sessions or private account data.
- Do not use the output for spam, harassment or unlawful discrimination.
- Check the target site's terms and local law before running at scale.
Checked against the Agent Skills format and the Actor's input schema: 15 of 15 checks pass
  • SKILL.md starts with YAML front matter
  • name is 1-64 lowercase letters, digits and single hyphens
  • name matches the skill folder name
  • description is present, at most 1024 characters and has no angle brackets
  • front matter uses only spec fields
  • compatibility is at most 500 characters
  • body has instructions
  • field descriptions end on whole sentences
  • no credential values are embedded
  • the token is read from APIFY_TOKEN
  • Example input: the schema has no required fields
  • Example input: every input key exists in the published input schema
  • Example input: every value has the type the schema declares
  • Example input: every choice field uses one of its published options
  • Example input: numbers respect the published minimum and maximum

Runner scriptyoutube-caption-and-whisper-ai-transcript-extractor/scripts/run.mjs

// Minimal runner: node scripts/run.mjs [input.json | '{"json": "input"}']
// Needs Node 18+ and APIFY_TOKEN. Without an argument it uses references/example-input.json.
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';

const token = process.env.APIFY_TOKEN;
if (!token) {
  console.error('Set APIFY_TOKEN');
  process.exit(1);
}
const actorId = process.env.ACTOR_ID || "nimbryxo-studio~youtube-caption-and-whisper-ai-transcript";
const here = path.dirname(fileURLToPath(import.meta.url));
const arg = process.argv[2];
const raw = !arg
  ? fs.readFileSync(path.join(here, '..', 'references', 'example-input.json'), 'utf8')
  : (arg.trim().startsWith('{') ? arg : fs.readFileSync(arg, 'utf8'));
const input = JSON.parse(raw);

const res = await fetch(`https://api.apify.com/v2/acts/${actorId}/run-sync-get-dataset-items`, {
  method: 'POST',
  headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}` },
  body: JSON.stringify(input)
});
if (!res.ok) {
  console.error(`HTTP ${res.status}: ${(await res.text()).slice(0, 500)}`);
  process.exit(1);
}
console.log(JSON.stringify(await res.json(), null, 2));

Example inputyoutube-caption-and-whisper-ai-transcript-extractor/references/example-input.json

{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "whisperModel": "tiny",
  "proxyCountry": "GB"
}

Using it

  • Have the agent read SKILL.md and adapt references/example-input.json for the requested videos, language and outputFormat. Review the Whisper settings and collection limits before using node scripts/run.mjs from the skill folder.

Prefer MCP? The YouTube Caption & Whisper AI Transcript Extractor page has configs for eight clients and a shorter, API-only SKILL.md.

Questions

Can the skill collect channel uploads or playlists?

Use mode set to channel with channelUrls, or playlist with playlistUrls. Bound collection with maxVideosPerChannel or maxVideosPerPlaylist.

How does the agent choose a transcript language?

Set language to request a caption language, or leave it empty to use the video's original language. Review transcript_language and was_translated to understand the returned text.

What happens when a video has no captions?

When useWhisper is enabled, the Actor can download the audio and attempt Whisper transcription. Use maxWhisperMinutes to limit which video lengths qualify, and inspect the returned status rather than assuming a transcript is available.

Are word timings available for every transcript?

No, wordTimings are available only from auto-generated YouTube captions when includeWordTimings is enabled. Manual captions and AI transcripts do not provide word timings.