# YouTube Transcript Scraper: Channel, Playlist & Keyword Search (`fulox/youtube-transcript-scraper`) Actor

YouTube transcript scraper: search a channel's transcripts, get timestamped quotes with links. Watch channels for new uploads with Slack alerts. RAG chunks, SRT, VTT. Videos, Shorts, playlists. From $1 per 1,000 transcript hours (a video under an hour counts once); failed videos free. No login.

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

## Pricing

from $1.00 / 1,000 transcript hours

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: Channel, Playlist & Keyword Search

Get the transcript of any YouTube video, Short, channel or playlist as clean text, timed segments, SRT, VTT, Markdown or RAG chunks. Then **search every transcript for your keywords** and get each hit as a quote with a link that opens the video at that second. Schedule it and **only new uploads** are delivered, with a digest in Slack.

**From $1.00 per 1,000 transcript hours (a video under an hour counts once). Videos without captions, listings and keyword matches are free.** No login, no API key. Built for RAG and AI-agent pipelines, content and SEO teams, brand and PR monitoring, and research.

[![Search a whole YouTube channel for your keywords and get timestamped quotes, new-upload alerts and RAG-ready chunks](https://raw.githubusercontent.com/FuloxDev/apify-assets/main/youtube-transcript-scraper/demo.gif)](https://github.com/FuloxDev/apify-assets/blob/main/youtube-transcript-scraper/demo.gif)

▶️ **[Watch the demo](https://github.com/FuloxDev/apify-assets/blob/main/youtube-transcript-scraper/demo.gif)**: paste a channel, search its transcripts, open each quote at the second it was said, then get only new uploads.

### ⚡ Try it in 30 seconds

Click **Start** with the default input. It reads two public videos (a 15-minute talk and a 19-second clip) and returns two transcripts for **$0.003** on the Free or Starter plan. Then paste your own links: videos, Shorts, `@channel` names or playlists.

### ✨ Key features

- 🔎 **Search a whole channel like Ctrl+F.** Put words in `searchTerms` and every hit comes back as a quote with the second it was said, a clickable `youtu.be/<id>?t=<seconds>` link and the chapter. A phrase that spans two caption lines still matches. Matches are free.

  ![Keyword quotes from a channel's transcripts, each with the second it was said and a link that opens the video there](https://raw.githubusercontent.com/FuloxDev/apify-assets/main/youtube-transcript-scraper/search-quotes.png)

- 🔔 **Watch mode.** Turn on `watch` and schedule the Actor. Each run delivers only the uploads it has not delivered before, plus a free digest row and an optional Slack or webhook message with the new videos and the top quotes.

- 🧠 **AI-ready output.** RAG `chunks` with a stable id (`<videoId>#3`), start and end seconds, a token estimate, the chapter and a deep link. Or one `markdown` document with YAML front matter and a `[m:ss](link)` anchor per paragraph. Clean text without `[Music]` and `>>`.

- 📑 **Chapters.** Read from the timestamps in the video description, and attached to every chunk and match.

- 🅰️ **Quality grade.** A to D from the caption kind (written by a person or auto-generated), how much of the video the captions cover, words per minute and the share of music cues, so you can filter weak transcripts before you embed them.

- 🔍 **YouTube searches as input.** Give `searchQueries` (what you would type into YouTube) and every query becomes a source: its first results are fetched like a channel's uploads. Listing is free.

- 📅 **Publish date for every video** (`publishedAt`, `YYYY-MM-DD`), also for links you paste directly.

- 📺 **Videos, Shorts, live recordings, channels and playlists** in one list, mixed. `channelContent` picks videos, Shorts or live streams of a channel. `publishedAfter` keeps recent ones.

- 🌍 **Languages in order of preference**, with a fallback to the best available track (the row says if your language was missing). Each row lists the languages the video has (`availableLanguages`).

- 👀 **Free preview.** `listOnly` returns one free row per video with its length, its caption languages and what it would cost, without downloading any transcript.

- 💶 **Fair pricing.** One result per started hour of video: a 10-minute video and a 59-minute one cost the same, a 3-hour podcast costs three. **Failures, listings, matches, digests and alerts are free.**

- 🔑 **No login, no API key, no personal data** beyond the public channel name.

### 👥 Who it is for

- 🤖 **AI and RAG builders.** Use case: load 300 videos of a conference channel into a vector store. Set `formats: ["chunks"]`; each chunk has a stable id, a token estimate and a deep link, so an answer can cite the exact second. Filter on `quality.grade` before embedding.
- ✍️ **Content repurposing.** Use case: turn a podcast back catalogue into articles and show notes. Set `formats: ["markdown"]` for a document with front matter, chapter headings and timestamp links per paragraph.
- 📈 **SEO and competitor research.** Use case: find every video where a competitor's channel mentions "pricing" or your product name. Set `searchTerms` and `formats: []` and read the quotes and links.
- 📣 **Brand and PR monitoring.** Use case: "tell me every time one of these 20 channels says our name". `watch` plus `searchTerms` on a daily Schedule, with the top quotes sent to Slack.
- 🔬 **Researchers and journalists.** Use case: export a playlist of hearings or lectures as text and find who said what, with a link to the moment.
- 🧩 **AI agents and MCP.** Use case: an agent answers "what did this channel say about X" with one call and cites `matches[].url`. See the agent notes below.

### 🚀 How to use it

1. Paste YouTube links into **Videos, channels or playlists**: `https://www.youtube.com/watch?v=...`, `https://youtu.be/...`, a Shorts link, `@mkbhd`, a channel link or a playlist link. Or type a search into **YouTube searches**.
2. Choose the **output formats** and, if you want quotes, add **search terms**.
3. Click **Start**. Download the dataset as JSON, CSV or Excel, or send it to Google Sheets, Make, Zapier, n8n or Clay.
4. For a recurring feed, turn on **Watch mode**, open **Schedules** and run the Actor daily or weekly with the same input. Each run delivers only new uploads.

Not sure what a channel will cost? Turn on `listOnly` first: it lists up to 200 videos with their lengths and caption languages, free.

### 📥 Input

![Input form with video, channel and playlist links, search terms, output formats and watch mode](https://raw.githubusercontent.com/FuloxDev/apify-assets/main/youtube-transcript-scraper/input.png)

Example input:

```json
{
  "videoUrls": ["@mkbhd", "https://www.youtube.com/watch?v=UF8uR6Z6KLc"],
  "searchQueries": ["steve jobs stanford commencement"],
  "searchTerms": ["battery life", "connecting the dots"],
  "languages": ["en"],
  "formats": ["text", "chunks"],
  "maxVideosPerSource": 20,
  "publishedAfter": "30 days"
}
```

| Field | What it does | Default |
|---|---|---|
| `videoUrls` | Videos, Shorts, `/live/` links, channels (`@handle`, `/channel/UC...`, `/c/name`, `/user/name`) and playlists (`list=PL...`). Up to 500 entries, 5,000 videos per run (300 on the Apify free plan). Duplicates and invalid entries are reported in free rows. Can be empty when you use `searchQueries`. | two public videos |
| `searchQueries` | Texts to search on YouTube (videos only, in YouTube's relevance order). Each query gives up to `maxVideosPerSource` videos (about 600 at most). Up to 20 queries of 200 characters. Listing is free. | none |
| `searchTerms` | Words or phrases to find in every transcript, ignoring case. Each hit becomes a quote with a timestamp link. Up to 50. | none |
| `matchRows` | Besides the video row, add one free `match` row per keyword hit (term, second, link, quote, chapter), up to 50 per video. | off |
| `languages` | Caption languages in order of preference (`en`, `de`, `pt-BR`). `en` also matches `en-US` and `en-GB`. | `["en"]` |
| `preferAutoGenerated` | Read the auto-generated track before the one written by a person. | off |
| `fallbackToAnyLanguage` | If none of your languages exists, deliver the best available track (`requestedLanguageFound: false`). Off: a free `language_not_found` row that lists what exists. | on |
| `formats` | `text`, `segments`, `srt`, `vtt`, `markdown`, `chunks`. Empty gives metadata, chapters, quality and matches only, at the same price. | `["text", "segments"]` |
| `chunkTokens` | Target chunk size, 100 to 2000 tokens. Chunks end at a sentence end or a pause of 2 seconds or more. | 400 |
| `channelContent` | For channels: `videos`, `shorts`, `live`. | `["videos"]` |
| `maxVideosPerSource` | Newest first, per playlist and per selected channel tab. | 100 |
| `publishedAfter` | A date (`2026-09-01`) or an age (`7 days`, `3 weeks`, `2 months`) for channels and playlists. Uses the listing's relative date ("3 days ago"). Shorts list no date, so their exact date is checked when they are fetched: an older Short comes back as a free `too_old` row. A video whose date cannot be found at all is kept. | none |
| `includeDescription` | Adds the video description (up to 5,000 characters). | off |
| `listOnly` | Free preview: one `preview` row per video with `hasCaptions`, languages, length and `estimatedResults`, no transcript, never charged. Checks at most 200 videos per run; watch mode is ignored. | off |
| `watch` | Deliver only videos not delivered by an earlier run with the same `watchlistName`. | off |
| `watchlistName` | Separate memory per name (letters, digits, `-`, `_`). | `default` |
| `slackWebhookUrl` / `webhookUrl` | A `hooks.slack.com` URL, or an https URL, that receives the digest when at least one transcript was delivered. Stored as secrets, never logged. | none |
| `resetState` | Forget what watch mode delivered for the sources in this run. | off |

### 📤 Output

![AI-ready output: RAG chunks with stable ids, token estimates, deep links and a quality grade](https://raw.githubusercontent.com/FuloxDev/apify-assets/main/youtube-transcript-scraper/ai-ready.png)

One dataset with five kinds of rows. **Filter the `type` column:** `video`, `match`, `input`, `digest`, `run` (and `preview` with `listOnly`). Fields a format did not ask for are `null`.

A delivered video (`type: "video"`, `status: "ok"`), shortened:

```json
{
  "type": "video", "status": "ok",
  "videoId": "UF8uR6Z6KLc", "url": "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
  "title": "Steve Jobs' 2005 Stanford Commencement Address", "channel": "Stanford", "lengthSeconds": 904, "durationText": "15:04",
  "viewCount": 49252055, "publishedAt": "2008-03-07", "language": "en", "languageName": "English", "captionKind": "manual", "requestedLanguageFound": true,
  "text": "This program is brought to you by Stanford University. Please visit us at stanford.edu\n\nThank You. I am honored to be with you today...",
  "segments": [{ "start": 7.47, "dur": 2.899, "text": "This program is brought to you by Stanford University." }],
  "chunks": [{ "id": "UF8uR6Z6KLc#1", "start": 7.47, "end": 118.469, "text": "This program is brought to you by...", "tokensApprox": 326, "chapter": null, "url": "https://youtu.be/UF8uR6Z6KLc?t=7" }],
  "chapters": [], "words": 2298, "tokensApprox": 3039,
  "quality": { "grade": "A", "captionKind": "manual", "coverage": 0.82, "wordsPerMinute": 185, "musicShare": 0, "reasons": ["manual captions", "speech covers 82% of the video"] },
  "matches": [{ "term": "connecting the dots", "start": 55.85, "timestamp": "0:55", "url": "https://youtu.be/UF8uR6Z6KLc?t=55", "quote": "...The first story is about connecting the dots. I dropped out of Reed College...", "chapter": null }],
  "matchCount": 1, "resultsCharged": 1
}
```

Other fields: `channelId`, `channelUrl`, `tags`, `thumbnailUrl`, `availableLanguages`, `translationLanguages` (information only: the languages YouTube lists as translatable; this Actor does not translate), `srt`, `vtt`, `markdown`, `description` (when asked), `source` (the channel or playlist the video came from) and `publishedText` (the listing's "3 days ago", for videos that came from a channel, playlist or search). `publishedAt` is the exact date for every delivered video; it comes from a second, tiny request, and is `null` if YouTube does not answer it.

- **A video that failed** has `status` `no_captions`, `language_not_found`, `unavailable`, `age_restricted`, `live_in_progress`, `blocked`, `pot_required`, `fetch_failed` or `too_large`, a `reason`, and `resultsCharged: 0`. Never charged.
- **`match` rows** (free, only with `matchRows`): one per keyword hit, right after the video row.
- **`preview` rows** (free, only with `listOnly`): `status: "preview"`, `hasCaptions`, `availableLanguages`, `lengthSeconds`, `estimatedResults`.
- **`input` rows** (free): invalid or duplicate entries, and for each channel or playlist a `source_resolved` row (`listed`, `afterFilters`, `alreadySeen`), or `source_not_found`, `source_empty`, `source_truncated`.
- **`digest` row** (free, last): the sources with counts, `videosDelivered`, `hoursOfVideo`, `resultsCharged`, `matchesTotal`, the 10 `topMatches`, up to 20 `newVideos` and the `alert` status.
- **`run` row** (free): says why nothing was delivered or why the run stopped early.

A run summary (`OUTPUT`) lists the counts and, for your own checks, the proxy bytes per video.

### 💸 Pricing

Pay per result, cheaper on higher Apify plans. **A result is one transcript of up to 60 minutes of video** (the Store calls it a "transcript hour"); a longer video counts one result per started hour (a 25-minute video is 1, a 2.5-hour podcast is 3).

| Your Apify plan | Per 1,000 results | Per result |
|---|---|---|
| Free and Starter | **$1.50** | $0.0015 |
| Scale | **$1.25** | $0.00125 |
| Business | **$1.00** | $0.0010 |
| Enterprise | **$1.00** | $0.0010 |

Apify's standard Store tiers apply: Free and Starter are the Free and Bronze tiers, Scale is Silver, Business is Gold, Enterprise is Platinum and Diamond.

**Free:** videos without captions, unavailable, age-restricted, live or refused videos, channel, playlist and search listings, `listOnly` previews, keyword matches and `match` rows, digests, alerts, and `input` and `run` rows.

| | Results | Free / Starter | Scale | Business |
|---|---|---|---|---|
| 🆕 The default input (2 short videos) | 2 | **$0.003** | $0.0025 | $0.002 |
| 📅 100 ten-minute videos | 100 | **$0.15** | $0.125 | $0.10 |
| 🎙️ One 3-hour podcast | 3 | **$0.0045** | $0.00375 | $0.003 |
| 📚 A 1,000-video channel, 10% without captions | about 900 | **$1.35** | $1.125 | $0.90 |
| 🔔 Weekly watch of 20 channels, 60 new uploads | 60 | **$0.09** | $0.075 | $0.06 |

Set a **maximum cost per run** to stay in control. A run on the Apify free plan processes up to 300 videos; paid plans up to 5,000.

### 🔌 Alerts and integrations

![Watch mode digest in Slack: new uploads from the watched channels and the top quotes](https://raw.githubusercontent.com/FuloxDev/apify-assets/main/youtube-transcript-scraper/channel-watch.png)

- 💬 **Schedule plus Slack:** `watch: true`, a `slackWebhookUrl`, and a daily Schedule. Slack gets one message per run with the new videos and the top quotes, and nothing when there is nothing new. Free.
- 🔗 **Webhook:** `webhookUrl` receives the same digest as JSON, no polling.
- 📑 **Google Sheets:** Integrations > Google Sheets appends the dataset after every run. Filter the `type` column to `video`.
- 🔁 **Make, n8n, Zapier:** run the Actor, then send `video` rows to your tool.
- 📓 **NotebookLM, Claude Projects, Cursor:** set `formats: ["markdown"]` and upload the `markdown` field of each row as a document.
- 🧱 **Clay:** an HTTP API column that calls this Actor's `run-sync-get-dataset-items` endpoint with your Apify token and the body `{"videoUrls":["{{url}}"],"formats":["text"]}`.
- 🛠️ **API:** call it from any language and read the dataset.

#### 🤖 For AI agents

Call with `{"videoUrls": ["https://www.youtube.com/watch?v=<id>"], "formats": ["text"]}` and read the dataset. Row `type` is `video` (`status: "ok"` has the transcript, any other status is a free failure with a `reason`), `match`, `preview`, `input`, `digest` or `run`. To work from a topic instead of links, pass `"searchQueries": ["your topic"]`. To check what exists before paying, add `"listOnly": true`; the `estimatedResults` of each `preview` row is what a real run would charge for it. Add `"matchRows": true` to get one flat row per quote. To find what someone said, add `"searchTerms": ["phrase"]` and `"formats": []`, then read `matches[].quote` and `matches[].url` (the link opens the video at that second). `quality.grade` A to D says how far to trust the text.

### 🆚 How it compares (October 2026)

Facts from each Actor's Apify Store page and README, read on 2026-10-14. Prices per 1,000 results are not like-for-like, and competitors may change.

| | This Actor | [starvibe/youtube-video-transcript](https://apify.com/starvibe/youtube-video-transcript) | [johnvc/YoutubeTranscripts](https://apify.com/johnvc/YoutubeTranscripts) | [pintostudio/youtube-transcript-scraper](https://apify.com/pintostudio/youtube-transcript-scraper) |
|---|---|---|---|---|
| Input | videos, Shorts, channels, playlists, searches, mixed, up to 500 entries | one video or one channel (max 200) per run | up to 100 URLs, channels (max 1,000) | one video |
| Playlists | yes | not documented | not documented | no |
| Keyword search with timestamp links | **yes** | no | no | no |
| New-uploads-only mode, digest, Slack | **yes** | no | no | no |
| RAG chunks, Markdown, chapters, quality grade | **yes** | no | no | no |
| SRT / VTT / text / segments | yes | segments, text | yes | segments |
| Translation | no | no | yes | no |
| Failed videos | free | charged as items | free | charged |
| Price per 1,000 | $1.50 Free/Starter, $1.25 Scale, $1.00 Business, per transcript hour | $5 | $2.75 down to $2.00 (+$3 for extended metadata) | $10 |

**Where others do better:** johnvc offers translation, a cache of popular videos and extended metadata. starvibe returns richer video metadata such as likes, and has more ratings. This Actor has no translation, no audio transcription and a shorter track record.

### ⚠️ Limitations

- **Captions only.** It reads the captions YouTube already has (written by the creator, or auto-generated). It does not transcribe audio, so a video without captions is a free `no_captions` row. Captions that hold only music or sound cues are treated the same way.
- **No translation.** You get the transcript in a language the video already has. `availableLanguages` shows which. `translationLanguages` only lists what YouTube says it could translate to; this Actor does not request translations.
- **YouTube can change.** Caption availability and the way YouTube serves it change from time to time. If YouTube refuses 8 requests in a row the run stops, writes a free `run` row, and charges nothing for the refused videos. Try again in a few minutes.
- **Dates:** `publishedAt` is the exact date of every delivered video (one small extra request). Videos from a channel, playlist or search also carry the listing's relative date in `publishedText` ("3 days ago"), and `publishedAfter` uses that one, rounded in your favour.
- **YouTube search** returns what YouTube returns: relevance order, at most about 600 results per query, and results can differ between runs and regions. Watch mode on a query delivers videos it has not delivered before, but a new video that ranks low may not be listed.
- **Live streams** are delivered after the stream has ended. A stream in progress is a free `live_in_progress` row.
- **Watch mode** remembers up to 5,000 video IDs per source. A video that failed for a temporary reason (`blocked`, `pot_required`, `fetch_failed`, `live_in_progress`, or a very fresh upload without captions yet) is tried again next run. The memory is saved with the run's progress (every 25 videos and whenever Apify moves or stops the run), so a hard kill delivers again at most the videos since the last save, up to about 25 plus the one in flight. A run that Apify restarts continues with the video list it saved at the start and never charges a video twice.
- **Very long captions:** captions over 2 MB are a free `too_large` row. A row over 8 MB drops `segments`, `vtt`, `srt` and `chunks` (in that order) and lists them in `truncated`, at the same price.
- **Search** is a case-insensitive text match, not meaning-based. Hyphens, spaces and punctuation between words count as the same, so "product-market fit" also finds "product market fit". At most 50 matches are listed per video; `matchCount` is the full count.
- Slack and webhook URLs must be https with a public host (Slack only `hooks.slack.com`). The host is also resolved, and an address that is private, loopback, link-local, shared (CGNAT) or reserved is refused before anything is sent.
- **`listOnly`** checks at most 200 videos per run.

### ❓ FAQ

**Is this legal?**
The Actor reads captions of public videos, the same text YouTube shows to anyone who turns subtitles on. It collects no comments and no personal data beyond the public channel name. You are responsible for how you use the text and for the laws that apply to you, including YouTube's terms and the creators' rights. It does not download any video or audio.

**What if YouTube changes something?**
Caption delivery on YouTube changes from time to time, and scrapers break when it does. If that happens, the run stops early, charges nothing for refused videos and says so in a free `run` row. Report it on the Issues tab and I fix it; I reply within 12 hours.

**What does "failed videos are free" cover?**
Everything that is not a delivered transcript: no captions, wrong language (with fallback off), private, deleted, age-restricted, live, refused by YouTube, a network failure, captions that are too large.

**How does watch mode work?**
At the end of each run the Actor saves the IDs it handled, per channel or playlist, in a key-value store named `youtube-transcript-scraper-state` in your account. The next run lists the source again and fetches only new IDs. A different `watchlistName` has its own memory. `resetState` clears it.

**Why is a video I pasted charged more than one result?**
It is longer than an hour. Each started hour is one result: 1:00:01 is two.

**How much will it cost me?**
$1.50 per 1,000 results (transcript hours) on Free and Starter, $1.25 on Scale, $1.00 on Business and Enterprise (see Pricing for worked examples). Use `listOnly` to see `estimatedResults` before a big run, and set a maximum cost per run.

**Can I get the transcript in another language than the video has?**
No, translation is not offered. See `availableLanguages` on each row for the languages the video has, and put your preferred ones in `languages`.

**How fast is it?**
About 6 videos are fetched in parallel. A hundred videos take a minute or two. A run can hold up to 5,000 videos.

**Something is wrong or missing. Who do I ask?**
Open an issue on the Actor's Issues tab. I reply within 12 hours.

### 🛠️ Need a custom feature?

A different output, a corpus file for a whole channel, a signal you need? **Open an issue** on the Actor page and describe it.

### Changelog

- 1.1.1 (2026-10-21): `publishedAfter` now also filters Shorts (older ones are a free `too_old` row, never charged); `publishedText` is kept on every listed video.
- 1.1 (2026-10-14): `searchQueries` (YouTube search as input), `publishedAt` for every video, `listOnly` free preview, `matchRows`, other YouTube clients tried before `pot_required`, a restarted run keeps its saved video list and never charges twice, watch memory saved with every progress save, alert hosts resolved and private addresses refused.
- 1.0 (2026-10-14): first version. Videos, Shorts, channels and playlists; text, segments, SRT, VTT, Markdown and RAG chunks; chapters; quality grade; keyword search with timestamp links; watch mode with digest, Slack and webhook; one result per started hour, failures free.

# Actor input Schema

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

Paste YouTube links, one per line: videos (watch?v=, youtu.be/, /shorts/, /live/, /embed/ or a bare 11-character ID), channels (https://www.youtube.com/@mkbhd, /channel/UC..., /c/name, /user/name or just @mkbhd) and playlists (a link with list=PL...). A channel or playlist is expanded into its newest videos (see 'Videos per channel or playlist'). Invalid entries and duplicates are reported in a free row. Leave empty when you use searchQueries. Up to 500 entries and 5,000 videos per run (300 on the Apify free plan).

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

Text to search on YouTube, one per line, as in the YouTube search box (videos only, in YouTube's relevance order). Each query is expanded into its first results, up to 'Videos per channel or playlist' per query (about 600 at most). Listing is free; only delivered transcripts are charged. Up to 20 queries of 200 characters. In watch mode each query remembers what it delivered.

## `searchTerms` (type: `array`):

Words or phrases to find in every transcript, ignoring case. Each hit becomes a quote with the second it was said, a clickable link that opens the video at that moment, and the chapter. A phrase may span two caption lines. Free: matches never change the price. Up to 50 terms of 100 characters.

## `matchRows` (type: `boolean`):

Besides the video row, add a free row of type match for every keyword hit (term, second, link that opens the video there, quote, chapter). Up to 50 per video. Matches never change the price.

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

Language codes such as en, de, es, pt-BR. The first language the video has wins; 'en' also matches en-US and en-GB. Up to 10.

## `preferAutoGenerated` (type: `boolean`):

By default, captions written by a person beat auto-generated ones in the same language. Turn on to read the auto-generated track first.

## `fallbackToAnyLanguage` (type: `boolean`):

On: a video without your languages gives its best available track and the row says requestedLanguageFound: false. Off: such a video is a free language_not_found row listing the languages it has.

## `formats` (type: `array`):

text: clean transcript in paragraphs. segments: timed lines (start, dur, text). srt, vtt: subtitle files as strings. markdown: one document with front matter, chapter headings and timestamp links. chunks: RAG chunks with a stable id, start and end, token estimate, chapter and deep link. Leave empty for metadata, chapters, quality and matches only (same price).

## `chunkTokens` (type: `integer`):

Target size of each chunk when 'chunks' is selected, 100 to 2000. Chunks end at a sentence end or a pause of 2 seconds or more and never split a caption line. Tokens are estimated as characters divided by 4.

## `channelContent` (type: `array`):

Which uploads of a channel to list: videos (long-form), shorts, live (past live streams).

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

Newest first. Applies to each playlist and to each selected channel tab. The run as a whole is capped at 5,000 videos (300 on the Apify free plan). Listing is free.

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

A date (2026-09-01) or an age (7 days, 24 hours, 3 weeks, 2 months). Uses the listing's relative date ('3 days ago'), so a video is kept when the date is unclear. Does not apply to videos you paste directly.

## `includeDescription` (type: `boolean`):

Adds the description (up to 5,000 characters) to each row. Chapters are read from the description either way.

## `listOnly` (type: `boolean`):

One free row per video with its length, caption languages and the results it would cost (estimatedResults), without downloading any transcript. Never charged. Checks at most 200 videos per run. Watch mode is ignored.

## `watch` (type: `boolean`):

Remembers which videos of each channel or playlist were delivered. Schedule the Actor and every run delivers only new uploads, plus a free digest row. Videos you paste directly are always processed.

## `watchlistName` (type: `string`):

Separate memory per name, so two schedules do not share what they have seen. Letters, digits, - and \_, up to 40 characters.

## `slackWebhookUrl` (type: `string`):

A hooks.slack.com incoming-webhook URL. A digest is posted when at least one transcript was delivered. Stored as a secret, never logged.

## `webhookUrl` (type: `string`):

An https URL that receives the digest as JSON when at least one transcript was delivered. Stored as a secret, never logged.

## `resetState` (type: `boolean`):

In watch mode, forget the remembered videos of the sources in this run, so everything is delivered (and charged) again.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=UF8uR6Z6KLc",
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "searchQueries": [],
  "searchTerms": [],
  "matchRows": false,
  "languages": [
    "en"
  ],
  "preferAutoGenerated": false,
  "fallbackToAnyLanguage": true,
  "formats": [
    "text",
    "segments"
  ],
  "chunkTokens": 400,
  "channelContent": [
    "videos"
  ],
  "maxVideosPerSource": 100,
  "includeDescription": false,
  "listOnly": false,
  "watch": false,
  "watchlistName": "default",
  "resetState": false
}
```

# Actor output Schema

## `videos` (type: `string`):

One row per video: transcript in the formats you chose, metadata, chapters, quality grade and keyword matches. Failed videos have a status and a reason and are never charged.

## `matches` (type: `string`):

One row per keyword hit with the second it is said and a link that opens the video there. Only with matchRows. Never charged.

## `failures` (type: `string`):

Videos with no captions, in another language, private, age-restricted, live or refused by YouTube, with the reason. Never charged.

## `inputs` (type: `string`):

Invalid or duplicate entries, how each channel or playlist was resolved, and why a run stopped early. Never charged.

## `digest` (type: `string`):

One summary row per run with the sources, hours of video, top keyword matches, new videos and the alert status.

## `summary` (type: `string`):

Counts for the run: videos delivered, results charged, proxy bytes per video, time and memory.

# 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=UF8uR6Z6KLc",
        "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    ]
};

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

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

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fulox/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/KnFNetVZuNe9439Hs/builds/2NgXQYTcXfWzTPpju/openapi.json
