# YouTube Search Transcripts Scraper 🔎 Topic to Text (`nokia2k/youtube-search-transcripts`) Actor

YouTube search transcript scraper: type a keyword and get the full transcripts and captions of the top-ranked YouTube videos for it, with views, likes and exact dates. Filter by upload date and length, export one Markdown file for AI research; no captions, no charge.

- **URL**: https://apify.com/nokia2k/youtube-search-transcripts.md
- **Developed by:** [nokia2k](https://apify.com/nokia2k) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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

**Type a topic and get the full transcripts of the YouTube videos that rank highest for it, with title, channel, views, likes and exact date, plus one Markdown file of everything for ChatGPT, Claude or NotebookLM.**

**$14.99 per 1,000 transcripts** at the time of writing, about one and a half cents each.

**Videos that fail or have no captions are never charged.**

🇪🇸 [Guía completa en español](#guía-completa-en-español)

### What does YouTube Search Transcripts Scraper do?

This Actor (Apify's word for a ready-made tool that runs on Apify's computers, not on yours) searches YouTube for your words, takes the videos YouTube ranks highest, reads their captions (the ones the uploader wrote, or the ones YouTube generates automatically) and gives you one row per video. No links to collect, no videos to watch. Nothing to set up: no YouTube API key, no Google account, no proxy.

#### Why people pick it

- **Words in, transcripts out.** Write `espresso machine review` as you would in YouTube's search box; get the transcripts of the top videos, up to 500 per search.
- **YouTube's own filters.** Upload date (last hour to this year), length (under 4 minutes to over 20), and "only videos with captions", on by default, so nearly every result comes back with text.
- **Every row says where it came from.** `sourceTitle` is the search that found the video and `positionInSource` its place, next to views, likes and the exact publish date.
- **One file for your AI assistant.** Every run writes `ALL_TRANSCRIPTS.md`: all transcripts in one document, each with title, channel, date and link.
- **Monitor a topic every week.** With **Only new videos since the last run**, a scheduled search delivers, and charges, only videos it has not delivered before.
- **You pay for transcripts, not for misses.** A video without captions or a search that finds nothing is a free row with its reason, and a run always ends as **Succeeded**.

#### Measured speed and reliability

- **Speed.** Two searches of five videos each took **8.1 seconds** from start to finish on the Apify platform (measured 7–8 October 2026). Larger runs take longer; 40 videos are worked on at the same time.
- **Transcripts.** In a test on 8 October 2026, 3,629 videos from 20 channels in two runs gave 3,447 transcripts, 171 videos without captions, 10 age-restricted videos and 1 video that failed on a timeout (0.03 %). None was blocked by YouTube.
- **Difficult videos.** 25 hard cases (no captions, live, age-restricted, private, deleted, paid, blocked in the US, a 5-hour video, Japanese, Korean, Spanish, Shorts) each got the right answer.
- **A run does not break your automation.** It ends as **Succeeded** with a row for every entry that says what happened. An unexpected problem becomes a row with `status: "error"`, and that row is not charged.
- **Before giving up on a video,** two separate app identities and three kinds of connection (home addresses, datacenter addresses and the server's own address) are tried. If Apify moves the run to another server, it continues where it stopped and never charges a video twice.

### Quick start: research a topic on YouTube in 5 steps

You have ten minutes before a meeting and need to know what YouTube says about a competitor's new product. You have never used Apify.

1. **Create a free account.** Open `https://console.apify.com/sign-up`. The quickest way is **Continue with Google**. Or type your email and a password of 8 characters or more, click **Sign up**, and then click the link in the email Apify sends you.
   - *You see:* Apify Console, the web panel where everything happens. The free plan gives you $5 to spend each month, with no credit card.
2. **Open the Actor.** Go to `https://apify.com/nokia2k/youtube-search-transcripts` and click **Try for free**. If you land somewhere else, click **Apify Store** in the left menu, search for `nokia2k youtube-search-transcripts` and click it.
   - *You see:* the **Input** tab. The first box, **🔎 What to search for**, holds an example: `how to learn spanish`. **🔢 Videos per search** shows 5.
3. **Type your topic** in place of the example, exactly as you would type it into YouTube. One search per line. Keep 5 videos for the first try: five transcripts cost about $0.08 at most.
4. **Click Start.**
   - *You see:* the status goes from RUNNING to SUCCEEDED, with a message such as "Done. 5 transcripts delivered." A **run** is one execution of the Actor; a small one takes seconds.
5. **Open the Output tab, click Export, choose a format and click Download.** Excel for a spreadsheet; also JSON, CSV, XML, HTML table, RSS and JSONL. **Preview** shows the result first.
   - *You see:* first one line per video with thumbnail, title, status, language, number of words, the transcript, channel, views, the search that found it, date, likes and a link. This table is called a **dataset**. After **Download** the file is in your Downloads folder.

The one-file version, `ALL_TRANSCRIPTS.md`, is on the run's **Storage** tab under **Key-value store** (the place where a run keeps its files): open it, download it, upload it to ChatGPT, Claude or NotebookLM and ask your question.

The smallest input, in JSON (the **Input** tab has a switch between the form and JSON):

```json
{
    "searchQueries": ["espresso machine review"],
    "maxVideosPerSearch": 5
}
```

To run it again, go back to the **Input** tab, change the words and click **Start**. Old runs are under **Runs** in the left menu. On the free plan Apify keeps an unnamed dataset for 7 days, so download what you need, or open the dataset, open the **Actions** menu and choose **Rename** to keep it.

### What data do you get from each video?

Each video found is one row. A search that finds nothing gives one row that says why. Rows are written as each video finishes, so the table is not in ranking order: sort by `sourceTitle` and then by `positionInSource` to see each search in order.

#### Every output field

| Field | Meaning | Example |
| --- | --- | --- |
| **Where it was found** | | |
| `source` | The search words, or the channel or playlist link, that led to this video. Empty for a video you pasted yourself. | `procrastination ted talk` |
| `sourceType` | `search`, `channel` or `playlist`. | `search` |
| `sourceTitle` | The search words, or the name of the channel or playlist. | `procrastination ted talk` |
| `positionInSource` | Place of the video in that search (after the order you chose), channel or playlist, starting at 1. | `1` |
| `publishedText` | The age of the video as YouTube's list shows it. | `3 weeks ago` |
| `input` | The value that produced the row. For a found video it is the video's link. | `https://www.youtube.com/watch?v=arj7oStGLkU` |
| **What happened** | | |
| `status` | The result for this video. Only `success` rows have a transcript. | `success` |
| `message` | A plain explanation when the status is not `success`, or a note such as a language fallback. | (empty) |
| `charged` | `true` when this row was charged. | `true` |
| `scrapedAt` | When the row was made, in UTC. | `2026-10-08T09:00:00+00:00` |
| **The video** | | |
| `videoId`, `url` | The 11-character YouTube ID and the link. | `arj7oStGLkU` |
| `title` | Video title. | `Inside the Mind of a Master Procrastinator` |
| `description`, `keywords` | The video description and the uploader's tags. | |
| `durationSeconds`, `viewCount` | Length in seconds; views at the time of the run. | `844`, `62013820` |
| `likeCount` | Exact number of likes. | `2054977` |
| `publishedAt` | Exact date and time of publication. | `2016-04-06T09:59:35-07:00` |
| `category` | YouTube category. | `People & Blogs` |
| `thumbnailUrl` | Largest thumbnail YouTube returns. | (image link) |
| `isShort`, `isLive` | `true` for Shorts; `true` for live streams and their recordings. | `false` |
| `liveStartedAt`, `liveEndedAt` | For live streams and recordings: when the stream started and ended. | `null` |
| `channelName`, `channelId` | Name and ID of the channel. | `TED`, `UCAuUUnT6oDeKwE6v1NGQxug` |
| **The language** | | |
| `language`, `languageName` | Code and name of the language returned. | `en`, `English` |
| `isAutoGenerated` | `true` when the captions come from YouTube's speech recognition, `false` when a person wrote them. | `false` |
| `isTranslated`, `translatedFrom` | Whether the text is YouTube's machine translation, and from which language. | `false`, `null` |
| `availableLanguages` | Every caption track the video has: `code`, `name`, `isAutoGenerated`, `isTranslatable`. | (list) |
| **The transcript** | | |
| `text` | The whole transcript as one paragraph. With the `text` format. | `So in college, I was a government major, …` |
| `timestampedText` | One line per caption: `[MM:SS] text`. With the `timestampedText` format. | `[00:01] All right, so here we are, …` |
| `segments` | Caption lines with `start`, `duration` and `end` in seconds, and `text`. | (list) |
| `srt`, `vtt` | Content of an `.srt` or `.vtt` subtitle file. | (text) |
| `markdown` | The transcript as a Markdown document with title, channel, date and link. | (text) |
| `files` | Download links of this video's files, by format. With `saveFiles` on. | (links) |
| `data` | Caption lines as `start`, `dur` and `text` strings. Only when the input used a single `videoUrl` field. | (list) |
| `segmentCount`, `wordCount`, `charCount` | Caption lines (or blocks), words and characters. | `315`, `2277`, `12671` |

#### Sample output: one success row and one no_captions row

With **📄 Output formats** set to Plain text. The video's values are real (8 October 2026). The search words, the position, the age text and `scrapedAt` are illustrative, and long values are cut with "…".

```json
{
    "videoId": "arj7oStGLkU",
    "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "status": "success",
    "message": "",
    "title": "Inside the Mind of a Master Procrastinator | Tim Urban | TED",
    "channelName": "TED",
    "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
    "durationSeconds": 844,
    "viewCount": 62013820,
    "publishedAt": "2016-04-06T09:59:35-07:00",
    "description": "…",
    "keywords": ["…"],
    "thumbnailUrl": "https://i.ytimg.com/vi/arj7oStGLkU/…",
    "isLive": false,
    "language": "en",
    "languageName": "English",
    "isAutoGenerated": false,
    "isTranslated": false,
    "translatedFrom": null,
    "availableLanguages": [
        { "code": "en", "name": "English", "isAutoGenerated": false, "isTranslatable": true }
    ],
    "segmentCount": 315,
    "wordCount": 2277,
    "charCount": 12671,
    "input": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "scrapedAt": "2026-10-08T09:00:00+00:00",
    "source": "procrastination ted talk",
    "sourceType": "search",
    "sourceTitle": "procrastination ted talk",
    "positionInSource": 1,
    "publishedText": "10 years ago",
    "category": "People & Blogs",
    "likeCount": 2054977,
    "isShort": false,
    "liveStartedAt": null,
    "liveEndedAt": null,
    "text": "So in college, I was a government major, which means I had to write a lot of papers. …",
    "charged": true
}
```

A video without captions found by the same search (possible when **Only videos that have captions** is off). The ID and the title are placeholders here; the other values are what such a row carries:

```json
{
    "videoId": "<11-character ID>",
    "url": "https://www.youtube.com/watch?v=<11-character ID>",
    "status": "no_captions",
    "message": "The video has no captions, neither uploaded nor auto-generated.",
    "title": "<title of the video>",
    "language": null,
    "availableLanguages": [],
    "wordCount": 0,
    "source": "procrastination ted talk",
    "sourceType": "search",
    "charged": false
}
```

#### The one-file Markdown export, ALL_TRANSCRIPTS.md

You want to ask an AI assistant "what do these ten reviewers agree on?". Uploading ten files is tedious; uploading a spreadsheet works badly. So every run also writes `ALL_TRANSCRIPTS.md`.

- **Where it is:** the run's **Storage** tab, **Key-value store**.
- **What is inside:** for every video with a transcript, a heading with the title, then up to four short lines (Channel, Published, URL, Language), then the transcript in paragraphs of about one minute of speech each. A line of three hyphens separates one video from the next.
- **Order:** videos appear in the order they finished, not in ranking order.
- **Size:** large runs are split into files of about 8 MB, named `ALL_TRANSCRIPTS_1.md`, `ALL_TRANSCRIPTS_2.md` and so on. In a very large run the files stop growing when they are full; the remaining transcripts are in the table only.
- **Price:** the file is free. It does not change what a transcript costs.

Markdown is plain text with a few marks for headings and lists. ChatGPT, Claude, NotebookLM and most knowledge bases read it well, and it opens in any text editor.

### Use cases

- **Know what reviewers praise and complain about** before you stock, buy or launch a product, without watching three hours of video.
- **See what already ranks for a keyword** and what those videos actually say, to plan your own video or article.
- **Brief yourself on a news story in minutes** with this week's videos only, newest on top.
- **Get a weekly digest of a topic in your inbox or Slack:** a scheduled search delivers only the new videos, and an AI step summarizes them.
- **Collect ten explanations of one concept** as plain text for study notes or a lesson.
- **Watch competitors' channels and a topic in one table,** with views, likes and exact dates for comparison.

### Input examples

#### Every input field

The boxes are described in the order of the form. Only the first one is needed. The name in `code` is the field name developers use.

**The search**

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **🔎 What to search for** (`searchQueries`) | What you would type into YouTube's search box. One search per line. | Always. Several lines give several searches in one run. | `iphone 17 review` |
| **🔢 Videos per search** (`maxVideosPerSearch`) | How many of the top results to take for each search. From 1 to 500. The form shows 5; when the field is left out in code, it is 10. | Raise it for a wider view; lower it to spend less. | `10` |

Write specific words: `standing desk review` rather than `standing desk`. Type the search in the language of the videos you want.

**Channels, playlists and videos (optional)**

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **📺 Channels, playlists or videos (optional)** (`urls`) | YouTube links, one per line. A channel link (or a plain `@name`) gives the channel's videos, newest first; a playlist link its videos; a video link that one video. | When you want specific channels, playlists or videos besides (or instead of) a search. | `https://www.youtube.com/@TED` |
| **🔢 Videos per channel or playlist** (`maxVideosPerSource`) | How many videos to take from each channel or playlist. From 1 to 5,000. The form shows 5; left out in code, it is 50. | When you paste a channel or a playlist. Start small. | `20` |

**Language and formats**

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **🌐 Transcript language** (`languages`) | Leave **Original language** and each video comes in the language it is spoken in. Or pick one or more languages in order of preference: the first one the video has is used. 70 languages in the list; you can also type a code (`pt-BR`) or a name (`Spanish`, `español`, `Deutsch`). Captions written by people are preferred. | When you want one language only, or a preferred one. | `["es", "en"]` |
| **📄 Output formats** (`outputFormats`) | `text`, `segments`, `timestampedText`, `srt`, `vtt`, `markdown`. Pick several; the price stays the same. Default: `segments` and `text`. | For reading and for AI, `text` is enough. | `["text"]` |
| **📚 Also give me everything as one Markdown file** (`combinedFile`) | Saves all transcripts of the run into `ALL_TRANSCRIPTS.md`. On by default. | Turn it off only if you never use the file. | `true` |
| **💾 Also save subtitle files I can download** (`saveFiles`) | A file per video for each text format you selected (`.srt`, `.vtt`, `.txt`), with download links in `files`. Off by default. | When you want files, not table cells. | `true` |

**🔎 Search filters** (they act on searches only)

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **📅 Uploaded** (`searchUploadDate`) | YouTube's own filter: Any time (`any`), Last hour (`hour`), Today (`today`), This week (`week`), This month (`month`), This year (`year`). | News, launches, anything where old videos mislead. | `week` |
| **⏱️ Length** (`searchDuration`) | YouTube's own filter: Any length (`any`), Under 4 minutes (`short`), 4 to 20 minutes (`medium`), Over 20 minutes (`long`). | `medium` or `long` for real reviews and explanations. | `medium` |
| **↕️ Order** (`searchSortBy`) | Most relevant first (`relevance`), Newest first (`date`), Most viewed first (`views`). Read "How the order really works" below. | When you want the found videos in another order. | `views` |
| **Only videos that have captions (recommended)** (`onlyWithCaptions`) | Asks YouTube for videos with captions only, so nearly every result comes back with a transcript. On by default. | See the note below. | `true` |

**About "Only videos that have captions".** This is YouTube's "Subtitles/CC" filter. YouTube applies it to videos whose uploader supplied captions, so videos that only have automatic captions are left out. With the box on, you get fewer misses and cleaner text. With the box off, you get a wider picture, small creators included: videos with automatic captions come back with their transcript, and videos with no captions at all are reported as `no_captions`, free of charge. For product and opinion research, try a run each way.

**📺 Channel and playlist filters** (they act on channels and playlists you pasted, not on searches)

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **📅 Only videos newer than** (`newerThan`) | A date, or an age such as `30 days` or `6 months`. Empty means no limit. Approximate: nothing newer is ever left out, and a few videos just past the limit can slip in. | To take only the recent videos of a channel. | `6 months` |
| **Only new videos since the last run** (`onlyNewVideos`) | For scheduled runs: each run delivers only the videos it has not delivered before for the same searches, channels and playlists. Off by default. | When you run it on a schedule. See [Run it on a schedule](#run-it-on-a-schedule-and-get-only-new-videos). | `true` |
| **Memory name** (`stateKey`) | Optional, used only with the box above: the name of the list of delivered videos. | To keep one list even when your searches change. | `my-weekly-topic` |
| **🎞️ Kinds of videos to take from a channel** (`contentTypes`) | Regular videos (`videos`), Shorts (`shorts`), recordings of live streams (`streams`). All three by default. | Untick what you do not need. | `["videos"]` |

**⚙️ More options** (the defaults suit most uses)

| Box | What it is | When to touch it | Example |
| --- | --- | --- | --- |
| **🔁 Translate to** (`translateTo`) | YouTube's own machine translation, used when the video has no captions in that language. YouTube does not offer it for every video; then you get the original language and a note in `message`. | You want everything in one language. | `es` |
| **If my language is missing, give me the original language** (`fallbackToAnyLanguage`) | On: a video without your languages still returns its transcript in its own language. Off: such videos are skipped, free of charge. | Another language is useless to you. | `false` |
| **Caption type** (`captionType`) | Any, human-made first (`any`); Human-made only (`manual`); Auto-generated only (`auto`). | `manual` when you need punctuation and correct names. | `manual` |
| **Merge caption lines into blocks** (`mergeSegmentsSeconds`) | Joins the short caption lines into blocks of about this many seconds. `0` keeps the original lines. Up to 3,600. | For search indexes and AI chunks. | `60` |
| **Include video details** (`includeMetadata`) | Adds channel, duration, view count, description, keywords and thumbnail. The title is always included. | Turn it off for smaller rows. | `false` |

#### Product research: praise and complaints, reviews of 4 to 20 minutes

```json
{
    "searchQueries": ["espresso machine review", "espresso machine problems"],
    "maxVideosPerSearch": 10,
    "searchDuration": "medium",
    "outputFormats": ["text"]
}
```

#### A news story: this week's videos, newest on top

```json
{
    "searchQueries": ["port strike"],
    "maxVideosPerSearch": 20,
    "searchUploadDate": "week",
    "searchSortBy": "date",
    "outputFormats": ["text"]
}
```

#### What already ranks, most viewed of the found videos first, with timestamps

```json
{
    "searchQueries": ["how to learn spanish"],
    "maxVideosPerSearch": 30,
    "searchSortBy": "views",
    "outputFormats": ["timestampedText"]
}
```

#### A search in Spanish, translated into English

```json
{
    "searchQueries": ["cafetera superautomática opiniones"],
    "maxVideosPerSearch": 10,
    "translateTo": "en",
    "outputFormats": ["text"]
}
```

#### A search plus one channel's videos of the last six months

```json
{
    "searchQueries": ["standing desk review"],
    "onlyWithCaptions": false,
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 20,
    "newerThan": "6 months",
    "contentTypes": ["videos"]
}
```

#### Weekly topic monitoring, only new videos

```json
{
    "searchQueries": ["espresso machine review", "espresso machine problems"],
    "maxVideosPerSearch": 20,
    "searchUploadDate": "month",
    "onlyNewVideos": true,
    "stateKey": "espresso-weekly",
    "outputFormats": ["text"]
}
```

#### How the order really works

- **Most relevant first** is YouTube's order. The videos arrive ranked as YouTube ranks them for that search.
- **Newest first** and **Most viewed first** do not ask YouTube for a different list. The Actor takes the same videos (the top results by relevance, as many as **🔢 Videos per search** says) and puts those videos in order of date or of views.

So "Most viewed first" with 10 videos gives you the 10 most relevant videos with the most viewed on top, not the 10 most viewed videos on YouTube for that topic. "Newest first" orders by the rough age YouTube's list shows ("3 weeks ago"), so two videos with the same age text keep YouTube's order between them; the exact date is in `publishedAt` on every row.

Two practical rules: want recent videos? Set **📅 Uploaded** to This week or This month, which really narrows the search, then add **Newest first** if you like. Want a fairer "most viewed" list? Raise **🔢 Videos per search**: the more videos are found, the more there is to put in order.

#### Where the search is made from

The search is made the way YouTube answers a visitor in the United States who uses YouTube in English and is not signed in. So your own YouTube page can show a slightly different list, because YouTube adapts results to each visitor. The videos can be in any language: `cafetera superautomática opiniones` finds Spanish videos, and each transcript comes in the language of its video. The rough age in `publishedText` is in English ("3 weeks ago"). There is no setting to choose another country.

### Run it on a schedule and get only new videos

A content creator wants a sheet that fills itself every Monday with what is new for her topic. This is how.

#### How "Only new videos since the last run" works

- Turn on **Only new videos since the last run** (`onlyNewVideos`). It is off by default and made for scheduled runs (Apify **Schedules**).
- Each run then delivers only the videos it has not delivered before for the same searches, channels and playlists. Videos already delivered are left out **before anything is fetched**, so they are neither fetched nor charged.
- The first run delivers everything it finds and remembers it.
- Video links you paste directly are always delivered, never left out. They are remembered, so a search or channel that finds them later does not deliver them again.

#### Set it up step by step

1. **Fill the form:** your searches, **🔢 Videos per search**, filters (for example **📅 Uploaded**: This month) and formats. Tick **Only new videos since the last run**. Optional: type a **Memory name** such as `espresso-weekly`.
2. **Click Start once by hand.** The first run delivers what it finds and remembers it. It also makes the Actor schedulable: an Actor must have run once before it can be scheduled.
3. **Click Save as a new task** at the top right of the Actor page. A **task** is a saved form; it appears under **Saved tasks**.
4. **Open Schedules** in the left menu and click **Create new**.
5. **In the Schedule setup card** choose how often (a cron expression such as `@weekly`, or the visual builder) and your time zone. **Next runs** shows the coming dates.
6. **In the Add dropdown** pick your task.
7. **Click Enable.** New schedules start disabled.

#### What you see in later runs

- The log and the run's final status message say how many videos were left out, for example: "Skipped 37 videos already delivered in earlier runs (memory 'x')."
- A run with nothing new ends normally, with no rows: "No new videos since the last run: … Nothing was charged."

#### Memory name: one list or several

- **Left empty:** the memory is named `auto-…` after your list of searches, channels and playlists. The order and the letter case of searches do not matter; any other change to the list starts a new memory.
- **With a name:** you pick the record. The same name keeps one list of delivered videos even when the searches or links change; a different name keeps a separate list.
- Letters, digits and `-_.!'()` are kept; other characters become `-`.

#### Where the memory lives and how to start afresh

The memory is kept in your own Apify account: **Storage** > **Key-value stores**, in the store named `youtube-transcripts-memory`. Each memory is one record that holds the IDs of the most recent 20,000 delivered videos and the time it was last updated. Delete the record, or the whole store, and the next run starts afresh and delivers everything again.

#### Good to know

- **Remembered:** videos delivered as `success` or `no_captions`. Everything else (`blocked`, `error`, `age_restricted`, a live stream that is still running or has not started) is tried again next run.
- **What counts as new:** each run searches again and looks at the top **🔢 Videos per search** results; the ones delivered before are left out. A video that enters the top results for the first time is new, even if it is old.
- **The memory is saved at the end of the run,** also when the run stops before its time limit or at the spending cap (then only the videos that were processed). After a server move the run continues and saves at its end; nothing is counted or charged twice.
- **If the memory cannot be read,** the run logs a warning and delivers all videos like a normal run. If it cannot be saved, the results are still delivered, and the next run may repeat some videos.
- **Two runs with the same memory at the same time** can both deliver the same new video. The memory itself keeps the videos of both.

#### Send new videos to Google Sheets or Slack

- **n8n:** add the **Apify Trigger** node (**Resource to Watch**: **Task**, your task; **Event Type**: **Succeeded**). Then an **Apify** node with Resource **Dataset**, **Get Items**, and the `defaultDatasetId` of the run as **Dataset ID** (raise **Limit**: it starts at 50). Then a Google Sheets node that appends one row per item (`sourceTitle`, `title`, `channelName`, `viewCount`, `url`), or an AI step that summarizes `text` and a Slack node that posts the digest. A self-hosted n8n needs a public `WEBHOOK_URL` so that Apify can reach it.
- **Make:** start with **Watch Actor Runs** (it fires when a run finishes), then **Get Dataset Items**, then a Google Sheets module (Apify's guide uses "Bulk Add Rows"), or a summary step and a Slack module.
- **Any other tool:** on the task's **Integrations** tab add a webhook for the event `ACTOR.RUN.SUCCEEDED`. Apify sends a POST to your address; the dataset ID is in `resource.defaultDatasetId`. The receiver must answer within 2 minutes, and a webhook can arrive twice.

A run with no new videos still finishes as Succeeded, with an empty dataset: your sheet simply gets no new rows that week.

### Use YouTube Search Transcripts Scraper from AI agents (MCP)

MCP is a standard that lets an AI assistant use outside tools. This address gives the assistant this Actor as a tool:

```text
https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts
```

Sign-in is OAuth: a browser window opens on the first connection and no token is stored in the configuration. A header `Authorization: Bearer <YOUR_APIFY_TOKEN>` works too. The assistant starts a run, waits (up to 45 seconds per call), then reads the rows. Keep assistant searches small, 5 to 10 videos. Each run is paid from your Apify plan like any other.

#### Claude.ai and Claude Desktop

1. Open Claude's connectors page (**Settings** > **Connectors**; in some versions **Customize** > **Connectors**).
2. Choose **Add custom connector**.
3. Fill **Name** (for example "YouTube search") and **Remote MCP server URL** with the address above. Click **Continue**, then **Add**.
4. Sign in to Apify when the browser asks.
5. In a chat, click **+**, then **Connectors**, and switch the connector on.

On the free Claude plan you can add one custom connector. On Team and Enterprise plans an Owner adds it for the organization.

#### Claude Desktop with a local server

`claude_desktop_config.json`, needs Node.js:

```json
{
  "mcpServers": {
    "youtube-search-transcripts": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--tools", "nokia2k/youtube-search-transcripts"],
      "env": {
        "APIFY_TOKEN": "<YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

#### Cursor

`.cursor/mcp.json` in your project, or `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "youtube-search-transcripts": {
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts"
    }
  }
}
```

With a token instead of OAuth, add `"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }` next to `"url"`.

#### VS Code

Needs GitHub Copilot. Open the Command Palette, run **MCP: Open User Configuration** and add:

```json
{
  "servers": {
    "youtube-search-transcripts": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts"
    }
  }
}
```

#### Claude Code and ChatGPT

```bash
claude mcp add --transport http youtube-search-transcripts \
  "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts" \
  --header "Authorization: Bearer $APIFY_TOKEN"
```

**ChatGPT:** Developer mode must be on. Go to **Settings > Apps & Connectors > Create**, fill **Name**, **Description** and **MCP Server URL** with the address above, leave **Authentication** on OAuth, and click **Create**. In a chat, click **+**, then **More**, and pick the connector.

#### Example prompts

- "Search YouTube for 'standing desk review', take the top 5 videos and tell me the three complaints reviewers repeat."
- "Find this month's top 10 videos about 'n8n automation tutorial' and list the tools each one uses."
- "Search 'how to learn spanish', read the 5 most relevant videos and write one study plan from what they agree on."

You can also simply upload `ALL_TRANSCRIPTS.md` to any assistant.

### Integrations: n8n, Make, Zapier, Python, JavaScript and LangChain

For most of these you need your **API token**: a secret key that lets another program use your Apify account. It is in Apify Console under **Settings**, **API & Integrations**. Treat it like a password. In the examples it is written `<YOUR_APIFY_TOKEN>`.

#### API: one call with curl

In web addresses the Actor is written `nokia2k~youtube-search-transcripts` (with a tilde); in the client libraries, `nokia2k/youtube-search-transcripts`. This call starts a run, waits and returns the rows. It waits up to 300 seconds; a longer run answers HTTP 408.

```bash
curl -X POST "https://api.apify.com/v2/acts/nokia2k~youtube-search-transcripts/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["espresso machine review"], "maxVideosPerSearch": 5, "outputFormats": ["text"]}'
```

The three-step flow for long runs:

1. `POST https://api.apify.com/v2/acts/nokia2k~youtube-search-transcripts/runs` with the input as JSON. The answer has the run's `id` and its `defaultDatasetId`. Add `?waitForFinish=60` to wait up to 60 seconds.
2. `GET https://api.apify.com/v2/actor-runs/<RUN_ID>` until `status` is `SUCCEEDED`.
3. `GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&clean=true`.

To cap spending from code, add the query parameter `maxTotalChargeUsd` (in Python, `max_total_charge_usd`).

#### Python

`pip install apify-client` (version 3, Python 3.11 or newer):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nokia2k/youtube-search-transcripts").call(
    run_input={
        "searchQueries": ["espresso machine review", "espresso machine problems"],
        "maxVideosPerSearch": 10,
        "outputFormats": ["text"],
    },
)
rows = list(client.dataset(run.default_dataset_id).iterate_items())
rows.sort(
    key=lambda row: (row.get("sourceTitle") or "", row.get("positionInSource") or 0)
)
for row in rows:
    if row["status"] == "success":
        print(
            row["sourceTitle"],
            row["positionInSource"],
            row["title"],
            row["wordCount"],
            "words",
        )
    else:
        print(row["input"], "->", row["status"], row["message"])
```

#### JavaScript

`npm install apify-client`:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('nokia2k/youtube-search-transcripts').call({
    searchQueries: ['how to learn spanish'],
    maxVideosPerSearch: 5,
    searchUploadDate: 'month',
    onlyWithCaptions: true,
    outputFormats: ['text'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items.filter((r) => r.status === 'success')) {
    console.log(row.positionInSource, row.title, row.channelName, row.url);
}
```

#### LangChain

`pip install langchain-apify langchain-core`, then set `APIFY_API_TOKEN` (the variable `langchain-apify` reads):

```python
from langchain_apify import ApifyWrapper
from langchain_core.documents import Document

loader = ApifyWrapper().call_actor(
    actor_id="nokia2k/youtube-search-transcripts",
    run_input={
        "searchQueries": ["espresso machine review"],
        "maxVideosPerSearch": 10,
        "outputFormats": ["text"],
    },
    dataset_mapping_function=lambda row: Document(
        page_content=row.get("text") or "",
        metadata={
            "source": row.get("url"),
            "title": row.get("title"),
            "query": row.get("sourceTitle"),
            "status": row.get("status"),
        },
    ),
)
docs = [d for d in loader.load() if d.metadata["status"] == "success"]
```

#### n8n

1. Install the Apify node. On n8n Cloud: open the canvas, open the nodes panel and search **Apify**; it appears under **More from the community**; install it. Self-hosted: **Settings > Community Nodes**, **Install**, type `@apify/n8n-nodes-apify`. It needs n8n 1.57.0 or higher.
2. Add the **Apify** node. Set Resource to **Actor** and Operation to **Run an Actor and Get Dataset**.
3. Create the credential **Apify API** and paste your token into **API Key**. On n8n Cloud you can use **Apify OAuth2 API** and **Connect my account** as well.
4. In **Actor**, choose YouTube Search Transcripts Scraper.
5. In **Input JSON**, paste `{"searchQueries": ["espresso machine review"], "maxVideosPerSearch": 5, "outputFormats": ["text"]}`. To search for a value from an earlier node, put an n8n expression in place of the words.
6. Set **Memory** to 4096, this Actor's default. The node sends 1024 unless you change it.
7. Optional: fill **Maximum Cost per Run (USD)**.
8. Execute the node.
   - *You see:* one n8n item per video. Connect the next node, for example one that summarizes `text`.

If the output looks odd or empty, you probably chose **Run an Actor**, which returns the run and not the transcripts. Use **Run an Actor and Get Dataset**, or add **Get Items** with the `defaultDatasetId` of the previous node as **Dataset ID**. **Get Items** returns 50 rows unless you raise **Limit**.

#### Make

1. Create a scenario and add the Apify module **Run an Actor**.
2. Create the **Connection** (sign in to Apify, or paste your token in the **API token** field).
3. Choose the **Actor**, set **Run synchronously** to **Yes**, and paste the same JSON into **Input JSON**.
4. Add the module **Get Dataset Items**. In **Dataset ID**, map the default dataset ID (`defaultDatasetId`) from the first module. If it is not offered, run the scenario once and look again. Raise **Limit** if you expect many rows.
5. Add the destination, for example a Google Sheets module.

Synchronous runs in Make are for short jobs. For runs longer than about 2 minutes, use **Watch Actor Runs** (it fires when a run finishes), then **Get Dataset Items**.

#### Zapier

Zapier waits at most 30 seconds for a synchronous run and loses the output after that. A small search can fit, but the safe way is two Zaps:

1. Zap A: your trigger (a schedule, a new sheet row), then the Apify action **Run Actor**, set to run asynchronously. Connect your account with **Allow Access**.
2. Zap B: the Apify trigger **Finished Actor Run**, then **Fetch Dataset Items**, then your destination.
3. Check each step with **Test Trigger** and **Test step**.

#### Google Sheets and Excel

- **Excel:** on the run's **Output** tab click **Export**, choose Excel, click **Download**, and open the file.
- **Google Sheets by hand:** export as CSV or Excel the same way and import the file in Google Sheets.
- **A tidy sheet:** in the export dialog, fill **Select fields** with the columns you want in the order you want them, for example `sourceTitle`, `positionInSource`, `title`, `channelName`, `viewCount`, `likeCount`, `publishedAt`, `url`, `text`.
- **Google Drive automatically:** on the Actor's **Integrations** tab choose **Upload results to GDrive**, click **Connect with Google**, set **Filename** and **Format**, and click **Save**. The file lands in the folder `Apify Uploads` after each successful run.
- **Google Sheets automatically:** use Make, Zapier or n8n as above, with their Google Sheets step last.

Long transcripts make very tall cells. For reading, the Markdown file is more comfortable than a sheet.

### How much does it cost to research a topic on YouTube?

The price is **$14.99 per 1,000 transcripts** at the time of writing. "Per 1,000" is only a way of showing a small number: each delivered transcript costs $0.01499, about one and a half cents, and you are charged one by one. Besides that there is only Apify's start fee: $0.00005 per run for each GB of memory, so $0.0002 for a run with the default 4 GB. The run itself costs nothing else: no platform usage, no proxy fees. The price that counts is the one on the Actor's **Pricing** tab.

| One run with | Transcripts delivered | Transcripts | Run start (4 GB) | Total |
| --- | --- | --- | --- | --- |
| 10 videos (one search) | 10 | $0.1499 | $0.0002 | **$0.1501** |
| 1,000 videos (for example 100 searches of 10) | 1,000 | $14.99 | $0.0002 | **$14.9902** |
| 10,000 videos (for example 20 searches of 500) | 10,000 | $149.90 | $0.0002 | **$149.9002** |

**The most a run can cost** is easy to work out before you press **Start**: searches × videos per search × $0.01499. Three searches of 10 videos with 3 videos without captions cost 27 × $0.01499 = $0.40. A weekly schedule with **Only new videos since the last run** that finds 32 new videos in four runs costs 32 × $0.01499 + 4 × $0.0002 = $0.48048 for the month.

**The free plan** gives $5 of credit each month with no card: about 330 transcripts a month at this price. Unused credit does not carry over.

**Never charged:** videos without captions; private, removed, paid or age-restricted videos; links that are not YouTube; a search, channel or playlist that could not be listed; videos YouTube refused or that failed; a video found twice in the same run (it is fetched once); videos left out because an earlier run delivered them; extra formats, the Markdown file, subtitle files, translation and video details. The `charged` field of every row says whether that row was charged.

**To cap spending:** before you click **Start**, open **Run options** and fill **Maximum cost per run**. The Actor stops at that amount and says so: "Stopped at the maximum charge you set for this run", with the number of videos not processed. Those are not charged.

### Errors and troubleshooting

#### What each status means

You asked for 10 videos and 8 rows say `success`. Look at the `status` and `message` columns of the other two.

| `status` | In plain words | What to do |
| --- | --- | --- |
| `success` | The transcript was delivered. This is the only status that is charged. | Nothing. |
| `no_captions` | The video has no captions at all, or it is a live stream that is still running. Common for music and for videos without speech. | Nothing can be read. For a live stream, run it again some hours after it ends. |
| `language_unavailable` | The video has captions, but not in the languages or caption type you chose, and the fallback is off. | Look at `availableLanguages`, then change **🌐 Transcript language**, turn the fallback on, or use **🔁 Translate to**. |
| `video_unavailable` | The video was deleted or never existed. | Check the link. |
| `video_unplayable` | Private, paid, blocked in the region or removed by YouTube. `message` has YouTube's reason. | Nothing can be read. |
| `age_restricted` | YouTube shows it only to signed-in adults. | Nothing can be read. |
| `blocked` | YouTube refused every connection that was tried. | Run it again later. It was not charged. |
| `error` | Something unexpected. `message` says what. | Run it again. If it repeats, write in the **Issues** tab. |
| `invalid_input` | The line is not a YouTube link or ID, or the form was empty. | Correct the line. Search words go in **🔎 What to search for**, links go in **📺 Channels, playlists or videos**. |
| `unsupported_url` | Rare here, because channels and playlists are accepted: a link that points to a list but is broken or incomplete. | Copy the link again from YouTube. |
| `source_problem` | A search, channel or playlist could not be listed. `message` says why, for example "YouTube found no videos for this search. Try fewer words or fewer filters." | Use fewer words, loosen the filters, or check the channel or playlist link. |

#### Problems and solutions

- **"These are not the results I see on YouTube."** The search is made as a signed-out visitor in the United States with YouTube in English; YouTube adapts results to each visitor.
- **"'Most viewed first' misses videos with more views."** That option orders the videos that were found. Raise **🔢 Videos per search** to widen the pool.
- **"Fewer results than I asked for."** YouTube may have fewer videos for those words and filters. A video found by two searches is kept once, under the first search that found it. Scheduled videos are left out. The memory leaves out videos delivered earlier. A spending cap or a timeout stops a run early, and its message says so.
- **"Many rows say no_captions."** Turn **Only videos that have captions** back on.
- **"The rows are not in ranking order."** Rows arrive as the videos finish. Sort by `sourceTitle`, then `positionInSource`.
- **"My weekly run delivered nothing."** "No new videos since the last run" in its message means the memory worked and nothing was charged.
- **"n8n gives odd output."** Use **Run an Actor and Get Dataset**, set **Memory** to 4096, and raise **Limit** in **Get Items**.
- **"Zapier lost the result."** Its synchronous runs stop after 30 seconds. Use two Zaps.

#### Limits to know

- **It does not transcribe audio.** A video with no captions at all returns `no_captions`.
- **Age-restricted, private and members-only videos** cannot be read.
- **It searches as a visitor in the United States with YouTube in English.** You cannot pick another country.
- **"Newest first" and "Most viewed first" put the found videos in order.** They are not YouTube's newest or most viewed videos on the topic.
- **"Only videos that have captions" leaves out videos that only have automatic captions.** Turn it off for the wider picture.
- **A search lists regular videos,** not channels, playlists or the Shorts shelf. Videos that are scheduled but have not started are left out.
- **At most 500 videos per search,** and YouTube can have fewer results than you ask for.
- **Translation is YouTube's machine translation** and is not offered for every video.
- **The date limit for channels and playlists is approximate.**
- **No comments, no dislikes, no subscriber counts** in this Actor.
- **Mixes, Watch later and Liked videos** cannot be listed.
- **Extremely long videos:** a row has a size limit. For a video of many hours the largest formats are left out of the row, and `message` says which. The plain text is kept.

### FAQ

#### Why did a YouTube video come back without a transcript?

Because YouTube has no captions for it, or does not show it to an anonymous visitor. The `status` and `message` of the row say which, and the table in [Errors and troubleshooting](#errors-and-troubleshooting) says what to do.

#### Am I charged when there is no result?

No. Only rows with `status: "success"` are charged. A search that finds nothing is free too. The `charged` field of every row confirms it.

#### What does "$14.99 per 1,000" mean, and what will my run cost?

It is a display unit. Each delivered transcript costs $0.01499. Multiply your searches by the videos per search and by that amount to get the most a run can cost: 3 searches of 10 videos are $0.45 at most.

#### Are these the same results I see on YouTube?

Close, not always identical. The search is made as a visitor in the United States with YouTube in English who is not signed in. YouTube adapts results to each visitor, so your own page can differ.

#### I chose "Most viewed first" and YouTube has videos with more views. Why?

Because that option puts the videos that were found in order of views. They are the most relevant results for your words, not the most viewed videos on YouTube. Raise **🔢 Videos per search** to widen the pool.

#### Can I search YouTube in Spanish, German or Japanese?

Yes. Type the search in the language of the videos you want. Each transcript comes in its video's language unless you choose another.

#### Which language do I get, and can it translate?

By default, the language spoken in each video. You can pick preferred languages, and **🔁 Translate to** asks for YouTube's machine translation when the video has no captions in your language. `language` on each row says what you received.

#### Does it transcribe the audio when there are no captions?

No. It reads captions that exist on YouTube, written by people or generated by YouTube. A video with neither returns `no_captions`, free of charge.

#### Does it work with YouTube Shorts?

Yes for a Short you paste, and for the Shorts of a channel you paste: keep Shorts ticked in **🎞️ Kinds of videos to take from a channel**. Every row says `isShort`. A search lists regular videos.

#### What about live streams?

A stream that is still running has no captions yet and returns `no_captions` with a message that says so. Run it again some hours after it ends. Recordings of finished streams work when YouTube has captions for them.

#### Can I give it a channel or a playlist?

Yes, in **📺 Channels, playlists or videos**, alone or next to your searches. If whole channels are all you need, [YouTube Channel Transcripts](https://apify.com/nokia2k/youtube-channel-transcripts) does that for a lower price.

#### Will YouTube block it? Do I need a proxy or a YouTube API key?

You need neither, and there is nothing to set up. Several kinds of connection are tried for each video. If YouTube still refuses, the row says `blocked`, costs nothing, and you can run it again later.

#### My run shows no data. Where are my results?

Open the run (left menu **Runs**, click the run) and its **Output** tab. If the table has one row with `invalid_input`, the form was empty. If it has a `source_problem` row, the search found nothing: read `message`. The Markdown file is on the **Storage** tab under **Key-value store**.

#### A field is missing or empty. Why?

Transcript fields exist only on `success` rows and only for the formats you selected. `files` needs **💾 Also save subtitle files I can download**. `source` and the position are empty for videos you pasted yourself. Details are empty when **Include video details** is off or when YouTube does not show a value.

#### How do the date filters work, and how exact are they?

For searches, **📅 Uploaded** is YouTube's own filter. For channels and playlists, **📅 Only videos newer than** works from the rough ages in YouTube's lists, so nothing newer is lost and a few slightly older videos can slip in. The exact date is always in `publishedAt`.

#### How do I get only new videos for a topic every week?

Turn on **Only new videos since the last run**, run once, save the form as a task and schedule it. Steps in [Run it on a schedule](#run-it-on-a-schedule-and-get-only-new-videos).

#### Why did I get fewer results than I asked for?

YouTube may have fewer videos for those words and filters. A video found by two searches is kept once. Scheduled videos are left out. With the memory on, videos delivered earlier are left out. A spending cap or a timeout also stops a run early, and the run's message says so.

#### How do I get the results into n8n, Make, Zapier or Google Sheets?

Follow the steps in [Integrations](#integrations-n8n-make-zapier-python-javascript-and-langchain). The short version: n8n has **Run an Actor and Get Dataset**; Make has **Run an Actor** plus **Get Dataset Items**; Zapier needs two Zaps; for a sheet, **Export** as Excel or CSV.

#### Can ChatGPT or Claude use it?

Yes, through Apify's MCP server at `https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts`. You can also simply upload `ALL_TRANSCRIPTS.md` to any assistant.

#### What do the timestamps mean, and what are SRT and VTT?

A timestamp says when a line is spoken. `segments` gives `start`, `duration` and `end` in seconds; `timestampedText` puts `[MM:SS]` before each line. SRT and VTT are the two common subtitle file types: SRT for video editors, VTT for web players.

#### Can I get comments, likes and the publish date?

Likes (`likeCount`), the exact publish date (`publishedAt`), category and views are on every row. Comments, chapters and channel details are in [YouTube Scraper](https://apify.com/nokia2k/youtube-all-in-one-scraper).

#### How many videos per run, and how many runs at once?

Up to 500 videos per search and 5,000 per channel or playlist, with as many lines as you like. How many runs you may have at the same time depends on your Apify plan: 32 on Starter, 128 on Scale and 256 on Business at the time of writing.

#### Is it legal? What about private videos and dislikes?

The Actor reads only what YouTube shows publicly to any visitor. It cannot read private videos, and dislikes are not available. What you do with the texts is your responsibility: respect copyright and YouTube's terms.

#### What do Actor, run, dataset and relevance mean?

| Word | Meaning |
| --- | --- |
| Actor | A ready-made tool that runs on Apify's computers. |
| Apify Console | Apify's web panel, where you fill the form and see results. |
| Input | What you give the Actor: the form. |
| Run | One execution of the Actor, from **Start** to the end. |
| Dataset | The table of results of a run. One row per video. |
| Key-value store | The place where a run keeps files, such as `ALL_TRANSCRIPTS.md`. |
| API, API token | The way programs, not people, talk to Apify, and your secret key for it. |
| Captions, transcript | The text YouTube can show under a video, and the captions of a whole video as one text. |
| Auto-generated captions | Captions made by YouTube's speech recognition. They can contain mistakes and often have no punctuation. |
| Relevance | YouTube's own ranking of results for a search. |
| SRT, VTT | Subtitle file types with the times of every line. |
| Markdown | Plain text with simple marks for headings and lists. AI tools read it well. |
| JSON | A text format for data that programs read. The form can be written as JSON. |
| Task, schedule | A saved form you can run again, and the timer that runs it. |
| MCP | A standard that lets AI assistants use outside tools. |
| Pay per event | You pay for each result delivered, not for time or a subscription. |

#### Something went wrong. Where do I get help?

In the **Issues** tab of this Actor. See [Support and updates](#support-and-updates).

### Related YouTube tools

Six Actors by the same developer, each made for a different starting point: pick the one that matches what you have. Prices per 1,000 results at the time of writing.

| Actor | Use it when you have | You get | Price |
| --- | --- | --- | --- |
| [YouTube Video to Text 📝](https://apify.com/nokia2k/youtube-video-to-text) | A big list of video links | Plain text only, the lowest price | $1.99 |
| [YouTube Transcript Scraper ⚡](https://apify.com/nokia2k/youtube-transcript-scraper) | Video links, and someone is waiting | Text, timestamps, SRT, VTT, translation, in about 3 s | $4.99 |
| [YouTube Channel Transcripts 📚](https://apify.com/nokia2k/youtube-channel-transcripts) | A channel, `@handle` or playlist | Every video, publish date, likes, one Markdown file, only new videos | $9.99 |
| **YouTube Search Transcripts Scraper 🔎 (you are here)** | A topic, no links | Transcripts of the videos YouTube ranks highest | $14.99 |
| [YouTube Scraper 🧰](https://apify.com/nokia2k/youtube-all-in-one-scraper) | Any of the above, and you need numbers | Transcript plus comments, chapters, most replayed moments, channel facts | $19.99 |
| [YouTube Transcript API 🚨](https://apify.com/nokia2k/youtube-transcript-instant-api) | An app that needs one video right now | An always-on web address, no start-up wait | $99.00 |

### Support and updates

Open an issue on the **Issues** tab of this Actor. To get a useful answer fast, include the link to the run (the address of the run's page in Apify Console), the search words or the video link that gave the problem, and what you expected and what you got.

Last updated: October 2026.

**Get your first transcripts: type one topic and click Start.**

### 🇪🇸 Guía completa en español

**Escribe un tema y recibe las transcripciones completas de los videos de YouTube que mejor posicionan para él, con título, canal, visualizaciones, «me gusta» y fecha exacta, más un archivo Markdown con todo para ChatGPT, Claude o NotebookLM.**

**$14.99 por cada 1000 transcripciones** en el momento de escribir esto, un centavo y medio cada una, aproximadamente.

**Los videos que fallan o no tienen subtítulos nunca se cobran.**

Este Actor (así llama Apify a una herramienta lista para usar que funciona en sus computadoras, no en la tuya) busca tus palabras en YouTube, toma los videos que YouTube posiciona más alto, lee sus subtítulos (los que escribió quien subió el video o los que YouTube genera automáticamente) y te da una fila por video. No hay enlaces que reunir ni videos que ver. No hay nada que configurar: ni clave de API de YouTube, ni cuenta de Google, ni proxy.

Las pantallas de Apify están en inglés. Por eso los botones y pestañas aparecen aquí en inglés y en negrita, con su significado entre comillas angulares la primera vez. Los nombres de los campos, los valores de `status` y el JSON también se quedan en inglés.

#### Por qué lo eligen

- **Entran palabras, salen transcripciones.** Escribe `espresso machine review` como lo harías en el buscador de YouTube; recibe las transcripciones de los mejores videos, hasta 500 por búsqueda.
- **Los filtros propios de YouTube.** Fecha de subida (de la última hora a este año), duración (de menos de 4 minutos a más de 20) y «solo videos con subtítulos», activado por defecto, para que casi todos los resultados traigan texto.
- **Cada fila dice de dónde viene.** `sourceTitle` es la búsqueda que encontró el video y `positionInSource`, su puesto, junto a las visualizaciones, los «me gusta» y la fecha exacta de publicación.
- **Un archivo para tu asistente de IA.** Cada ejecución escribe `ALL_TRANSCRIPTS.md`: todas las transcripciones en un documento, cada una con título, canal, fecha y enlace.
- **Vigila un tema cada semana.** Con **Only new videos since the last run** («Solo videos nuevos desde la última ejecución»), una búsqueda programada entrega, y cobra, solo los videos que no había entregado antes.
- **Pagas transcripciones, no fallos.** Un video sin subtítulos o una búsqueda sin resultados es una fila gratuita con su motivo, y una ejecución siempre termina como **Succeeded** («completada»).

#### Velocidad y fiabilidad medidas

- **Velocidad.** Dos búsquedas de cinco videos cada una tardaron **8.1 segundos** de principio a fin en la plataforma de Apify (medido el 7 y 8 de octubre de 2026). Las ejecuciones más grandes tardan más; se trabaja con 40 videos a la vez.
- **Transcripciones.** En una prueba del 8 de octubre de 2026, 3629 videos de 20 canales en dos ejecuciones dieron 3447 transcripciones, 171 videos sin subtítulos, 10 videos con restricción de edad y 1 video fallido por tiempo agotado (0.03 %). YouTube no bloqueó ninguno.
- **Videos difíciles.** 25 casos complicados (sin subtítulos, en directo, con restricción de edad, privados, borrados, de pago, bloqueados en Estados Unidos, un video de 5 horas, en japonés, coreano y español, Shorts) recibieron cada uno la respuesta correcta.
- **Una ejecución no rompe tu automatización.** Termina como **Succeeded** con una fila por cada entrada que dice qué pasó. Un problema inesperado se convierte en una fila con `status: "error"`, y esa fila no se cobra.
- **Antes de rendirse con un video,** se prueban dos identidades de aplicación distintas y tres tipos de conexión (direcciones domésticas, direcciones de centros de datos y la dirección del propio servidor). Si Apify traslada la ejecución a otro servidor, continúa donde se quedó y nunca cobra un video dos veces.

### Inicio rápido: investiga un tema en YouTube en 5 pasos

Tienes diez minutos antes de una reunión y necesitas saber qué dice YouTube sobre el nuevo producto de un competidor. Nunca has usado Apify.

1. **Crea una cuenta gratuita.** Abre `https://console.apify.com/sign-up`. Lo más rápido es **Continue with Google** («Continuar con Google»). O escribe tu correo y una contraseña de 8 caracteres o más, haz clic en **Sign up** («Registrarse») y después en el enlace del correo que te envía Apify.
   - *Verás:* Apify Console («Consola de Apify»), el panel web donde ocurre todo. El plan gratuito te da $5 para gastar cada mes, sin tarjeta.
2. **Abre el Actor.** Entra en `https://apify.com/nokia2k/youtube-search-transcripts` y haz clic en **Try for free** («Probar gratis»). Si llegas a otra página, haz clic en **Apify Store** («tienda de Apify») en el menú de la izquierda, busca `nokia2k youtube-search-transcripts` y haz clic en él.
   - *Verás:* la pestaña **Input** («Entrada»). La primera casilla, **🔎 What to search for** («Qué buscar»), trae un ejemplo: `how to learn spanish`. **🔢 Videos per search** («Videos por búsqueda») muestra 5.
3. **Escribe tu tema** en lugar del ejemplo, tal como lo escribirías en YouTube. Una búsqueda por línea. Deja 5 videos para la primera prueba: cinco transcripciones cuestan como mucho unos $0.08.
4. **Haz clic en Start** («Iniciar»).
   - *Verás:* el estado pasa de RUNNING («en marcha») a SUCCEEDED, con un mensaje como "Done. 5 transcripts delivered." Una **ejecución** es cada vez que el Actor trabaja; una pequeña tarda segundos.
5. **Abre la pestaña Output** («Salida»), **haz clic en Export** («Exportar»), **elige un formato y haz clic en Download** («Descargar»). Excel para una hoja de cálculo; también JSON, CSV, XML, HTML table, RSS y JSONL. **Preview** («Vista previa») te enseña el resultado antes.
   - *Verás:* primero una línea por video con miniatura, título, estado, idioma, número de palabras, la transcripción, canal, visualizaciones, la búsqueda que lo encontró, fecha, «me gusta» y un enlace. Esta tabla se llama **dataset** («conjunto de resultados»). Después de **Download**, el archivo está en tu carpeta Descargas.

La versión en un solo archivo, `ALL_TRANSCRIPTS.md`, está en la pestaña **Storage** («Almacenamiento») de la ejecución, en **Key-value store** («almacén de archivos», el lugar donde una ejecución guarda sus archivos): ábrelo, descárgalo, súbelo a ChatGPT, Claude o NotebookLM y haz tu pregunta.

La entrada más pequeña, en JSON (la pestaña **Input** tiene un selector entre el formulario y JSON):

```json
{
    "searchQueries": ["espresso machine review"],
    "maxVideosPerSearch": 5
}
```

Para ejecutarlo otra vez, vuelve a la pestaña **Input**, cambia las palabras y haz clic en **Start**. Las ejecuciones anteriores están en **Runs** («Ejecuciones») en el menú de la izquierda. En el plan gratuito, Apify guarda un dataset sin nombre durante 7 días, así que descarga lo que necesites, o abre el dataset, abre el menú **Actions** («Acciones») y elige **Rename** («Cambiar nombre») para conservarlo.

### Qué datos recibes de cada video

Cada video encontrado es una fila. Una búsqueda que no encuentra nada da una fila que dice por qué. Las filas se escriben a medida que termina cada video, así que la tabla no sigue el orden del ranking: ordena por `sourceTitle` y después por `positionInSource` para ver cada búsqueda en orden.

#### Todos los campos de salida

| Campo | Qué significa | Ejemplo |
| --- | --- | --- |
| **Dónde se encontró** | | |
| `source` | Las palabras de búsqueda, o el enlace del canal o de la lista, que llevaron a este video. Vacío en un video que pegaste tú. | `procrastination ted talk` |
| `sourceType` | `search`, `channel` o `playlist`. | `search` |
| `sourceTitle` | Las palabras de búsqueda, o el nombre del canal o de la lista. | `procrastination ted talk` |
| `positionInSource` | Puesto del video en esa búsqueda (tras el orden que elegiste), canal o lista, empezando en 1. | `1` |
| `publishedText` | La antigüedad del video tal como la muestra la lista de YouTube. | `3 weeks ago` |
| `input` | El valor que produjo la fila. En un video encontrado, es el enlace del video. | `https://www.youtube.com/watch?v=arj7oStGLkU` |
| **Qué pasó** | | |
| `status` | El resultado para este video. Solo las filas `success` tienen transcripción. | `success` |
| `message` | Una explicación sencilla cuando el estado no es `success`, o una nota, por ejemplo sobre un cambio de idioma. | (vacío) |
| `charged` | `true` cuando esta fila se cobró. | `true` |
| `scrapedAt` | Cuándo se creó la fila, en UTC. | `2026-10-08T09:00:00+00:00` |
| **El video** | | |
| `videoId`, `url` | El identificador de 11 caracteres y el enlace. | `arj7oStGLkU` |
| `title` | Título del video. | `Inside the Mind of a Master Procrastinator` |
| `description`, `keywords` | La descripción del video y las etiquetas de quien lo subió. | |
| `durationSeconds`, `viewCount` | Duración en segundos; visualizaciones en el momento de la ejecución. | `844`, `62013820` |
| `likeCount` | Número exacto de «me gusta». | `2054977` |
| `publishedAt` | Fecha y hora exactas de publicación. | `2016-04-06T09:59:35-07:00` |
| `category` | Categoría de YouTube. | `People & Blogs` |
| `thumbnailUrl` | La miniatura más grande que devuelve YouTube. | (enlace de imagen) |
| `isShort`, `isLive` | `true` para los Shorts; `true` para directos y sus grabaciones. | `false` |
| `liveStartedAt`, `liveEndedAt` | En directos y grabaciones: cuándo empezó y terminó la emisión. | `null` |
| `channelName`, `channelId` | Nombre e identificador del canal. | `TED`, `UCAuUUnT6oDeKwE6v1NGQxug` |
| **El idioma** | | |
| `language`, `languageName` | Código y nombre del idioma devuelto. | `en`, `English` |
| `isAutoGenerated` | `true` cuando los subtítulos vienen del reconocimiento de voz de YouTube, `false` cuando los escribió una persona. | `false` |
| `isTranslated`, `translatedFrom` | Si el texto es la traducción automática de YouTube, y desde qué idioma. | `false`, `null` |
| `availableLanguages` | Todas las pistas de subtítulos del video: `code`, `name`, `isAutoGenerated`, `isTranslatable`. | (lista) |
| **La transcripción** | | |
| `text` | Toda la transcripción como un párrafo. Con el formato `text`. | `So in college, I was a government major, …` |
| `timestampedText` | Una línea por subtítulo: `[MM:SS] texto`. Con el formato `timestampedText`. | `[00:01] All right, so here we are, …` |
| `segments` | Líneas de subtítulos con `start`, `duration` y `end` en segundos, y `text`. | (lista) |
| `srt`, `vtt` | Contenido de un archivo de subtítulos `.srt` o `.vtt`. | (texto) |
| `markdown` | La transcripción como documento Markdown con título, canal, fecha y enlace. | (texto) |
| `files` | Enlaces de descarga de los archivos de este video, por formato. Con `saveFiles` activado. | (enlaces) |
| `data` | Líneas de subtítulos como cadenas `start`, `dur` y `text`. Solo cuando la entrada usó un campo único `videoUrl`. | (lista) |
| `segmentCount`, `wordCount`, `charCount` | Líneas de subtítulos (o bloques), palabras y caracteres. | `315`, `2277`, `12671` |

#### Ejemplo de salida: una fila correcta y una fila no_captions

Con **📄 Output formats** («Formatos de salida») en texto plano. Los valores del video son reales (8 de octubre de 2026). Las palabras de búsqueda, el puesto, el texto de antigüedad y `scrapedAt` son ilustrativos, y los valores largos se cortan con "…".

```json
{
    "videoId": "arj7oStGLkU",
    "url": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "status": "success",
    "message": "",
    "title": "Inside the Mind of a Master Procrastinator | Tim Urban | TED",
    "channelName": "TED",
    "channelId": "UCAuUUnT6oDeKwE6v1NGQxug",
    "durationSeconds": 844,
    "viewCount": 62013820,
    "publishedAt": "2016-04-06T09:59:35-07:00",
    "description": "…",
    "keywords": ["…"],
    "thumbnailUrl": "https://i.ytimg.com/vi/arj7oStGLkU/…",
    "isLive": false,
    "language": "en",
    "languageName": "English",
    "isAutoGenerated": false,
    "isTranslated": false,
    "translatedFrom": null,
    "availableLanguages": [
        { "code": "en", "name": "English", "isAutoGenerated": false, "isTranslatable": true }
    ],
    "segmentCount": 315,
    "wordCount": 2277,
    "charCount": 12671,
    "input": "https://www.youtube.com/watch?v=arj7oStGLkU",
    "scrapedAt": "2026-10-08T09:00:00+00:00",
    "source": "procrastination ted talk",
    "sourceType": "search",
    "sourceTitle": "procrastination ted talk",
    "positionInSource": 1,
    "publishedText": "10 years ago",
    "category": "People & Blogs",
    "likeCount": 2054977,
    "isShort": false,
    "liveStartedAt": null,
    "liveEndedAt": null,
    "text": "So in college, I was a government major, which means I had to write a lot of papers. …",
    "charged": true
}
```

Un video sin subtítulos encontrado por la misma búsqueda (posible cuando **Only videos that have captions** está desactivado). El identificador y el título son marcadores; los demás valores son los que lleva una fila así:

```json
{
    "videoId": "<11-character ID>",
    "url": "https://www.youtube.com/watch?v=<11-character ID>",
    "status": "no_captions",
    "message": "The video has no captions, neither uploaded nor auto-generated.",
    "title": "<title of the video>",
    "language": null,
    "availableLanguages": [],
    "wordCount": 0,
    "source": "procrastination ted talk",
    "sourceType": "search",
    "charged": false
}
```

#### La exportación en un solo archivo Markdown, ALL_TRANSCRIPTS.md

Quieres preguntar a un asistente de IA «¿en qué coinciden estos diez analistas?». Subir diez archivos es pesado; subir una hoja de cálculo funciona mal. Por eso cada ejecución escribe también `ALL_TRANSCRIPTS.md`.

- **Dónde está:** pestaña **Storage** de la ejecución, **Key-value store**.
- **Qué contiene:** para cada video con transcripción, un título, después hasta cuatro líneas cortas (Channel, Published, URL, Language) y después la transcripción en párrafos de alrededor de un minuto de habla cada uno. Una línea de tres guiones separa un video del siguiente.
- **Orden:** los videos aparecen en el orden en que terminaron, no en el del ranking.
- **Tamaño:** las ejecuciones grandes se dividen en archivos de unos 8 MB, llamados `ALL_TRANSCRIPTS_1.md`, `ALL_TRANSCRIPTS_2.md` y así sucesivamente. En una ejecución muy grande, los archivos dejan de crecer cuando están llenos; las transcripciones restantes están solo en la tabla.
- **Precio:** el archivo es gratis. No cambia lo que cuesta una transcripción.

Markdown es texto plano con unas pocas marcas para títulos y listas. ChatGPT, Claude, NotebookLM y la mayoría de las bases de conocimiento lo leen bien, y se abre con cualquier editor de texto.

### Casos de uso

- **Descubre qué elogian y qué critican los analistas** antes de tener en stock, comprar o lanzar un producto, sin ver tres horas de video.
- **Mira qué posiciona ya para una palabra clave** y qué dicen de verdad esos videos, para planificar tu propio video o artículo.
- **Ponte al día de una noticia en minutos** con los videos de esta semana, primero los más nuevos.
- **Recibe cada semana un resumen de un tema en tu correo o en Slack:** una búsqueda programada entrega solo los videos nuevos y un paso de IA los resume.
- **Reúne diez explicaciones de un concepto** como texto plano para apuntes o una clase.
- **Vigila los canales de la competencia y un tema en una sola tabla,** con visualizaciones, «me gusta» y fechas exactas para comparar.

### Ejemplos de entrada

#### Todos los campos de entrada

Las casillas se describen en el orden del formulario. Solo la primera es necesaria. El nombre en `code` es el que usan los desarrolladores.

**La búsqueda**

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **🔎 What to search for** (`searchQueries`) | Lo que escribirías en el buscador de YouTube. Una búsqueda por línea. | Siempre. Varias líneas dan varias búsquedas en una ejecución. | `iphone 17 review` |
| **🔢 Videos per search** (`maxVideosPerSearch`) | Cuántos de los primeros resultados tomar de cada búsqueda. De 1 a 500. El formulario muestra 5; si el campo falta en el código, es 10. | Súbelo para una visión más amplia; bájalo para gastar menos. | `10` |

Escribe palabras concretas: `standing desk review` mejor que `standing desk`. Escribe la búsqueda en el idioma de los videos que quieres.

**Canales, listas de reproducción y videos (opcional)**

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **📺 Channels, playlists or videos (optional)** (`urls`) | «Canales, listas o videos (opcional)». Enlaces de YouTube, uno por línea. Un enlace de canal (o un simple `@nombre`) da los videos del canal, primero los más nuevos; un enlace de lista, sus videos; un enlace de video, ese video. | Cuando quieres canales, listas o videos concretos además de una búsqueda (o en su lugar). | `https://www.youtube.com/@TED` |
| **🔢 Videos per channel or playlist** (`maxVideosPerSource`) | Cuántos videos tomar de cada canal o lista. De 1 a 5000. El formulario muestra 5; si falta en el código, es 50. | Cuando pegas un canal o una lista. Empieza con poco. | `20` |

**Idioma y formatos**

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **🌐 Transcript language** (`languages`) | «Idioma de la transcripción». Deja **Original language** («Idioma original») y cada video llega en el idioma en que se habla. O elige uno o varios idiomas por orden de preferencia: se usa el primero que tenga el video. 70 idiomas en la lista; también puedes escribir un código (`pt-BR`) o un nombre (`Spanish`, `español`, `Deutsch`). Se prefieren los subtítulos escritos por personas. | Cuando quieres un solo idioma, o uno preferido. | `["es", "en"]` |
| **📄 Output formats** (`outputFormats`) | `text`, `segments`, `timestampedText`, `srt`, `vtt`, `markdown`. Elige varios; el precio no cambia. Por defecto: `segments` y `text`. | Para leer y para IA, basta con `text`. | `["text"]` |
| **📚 Also give me everything as one Markdown file** (`combinedFile`) | «Dame también todo en un archivo Markdown». Guarda todas las transcripciones de la ejecución en `ALL_TRANSCRIPTS.md`. Activado por defecto. | Desactívalo solo si nunca usas el archivo. | `true` |
| **💾 Also save subtitle files I can download** (`saveFiles`) | «Guardar también archivos de subtítulos que pueda descargar»: un archivo por video para cada formato de texto elegido (`.srt`, `.vtt`, `.txt`), con enlaces de descarga en `files`. Desactivado por defecto. | Cuando quieres archivos, no celdas de una tabla. | `true` |

**🔎 Search filters** («Filtros de búsqueda»; solo actúan sobre las búsquedas)

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **📅 Uploaded** (`searchUploadDate`) | «Fecha de subida», el filtro propio de YouTube: Any time («Cualquier momento», `any`), Last hour («Última hora», `hour`), Today («Hoy», `today`), This week («Esta semana», `week`), This month («Este mes», `month`), This year («Este año», `year`). | Noticias, lanzamientos, todo lo que los videos antiguos distorsionan. | `week` |
| **⏱️ Length** (`searchDuration`) | «Duración», el filtro propio de YouTube: Any length («Cualquiera», `any`), Under 4 minutes («Menos de 4 minutos», `short`), 4 to 20 minutes («De 4 a 20 minutos», `medium`), Over 20 minutes («Más de 20 minutos», `long`). | `medium` o `long` para análisis y explicaciones de verdad. | `medium` |
| **↕️ Order** (`searchSortBy`) | «Orden»: Most relevant first («Más relevantes primero», `relevance`), Newest first («Más nuevos primero», `date`), Most viewed first («Más vistos primero», `views`). Lee «Cómo funciona de verdad el orden» más abajo. | Cuando quieres los videos encontrados en otro orden. | `views` |
| **Only videos that have captions (recommended)** (`onlyWithCaptions`) | «Solo videos con subtítulos (recomendado)». Pide a YouTube solo videos con subtítulos, así que casi todos los resultados traen transcripción. Activado por defecto. | Mira la nota de abajo. | `true` |

**Sobre «Only videos that have captions».** Es el filtro «Subtítulos/CC» de YouTube. YouTube lo aplica a los videos cuyo autor subió subtítulos, así que se quedan fuera los videos que solo tienen subtítulos automáticos. Con la casilla activada, tienes menos fallos y un texto más limpio. Con la casilla desactivada, tienes una visión más amplia, con creadores pequeños incluidos: los videos con subtítulos automáticos vuelven con su transcripción, y los que no tienen ningún subtítulo aparecen como `no_captions`, sin coste. Para investigar productos y opiniones, prueba una ejecución de cada forma.

**📺 Channel and playlist filters** («Filtros de canales y listas»; actúan sobre los canales y listas que pegaste, no sobre las búsquedas)

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **📅 Only videos newer than** (`newerThan`) | «Solo videos más recientes que»: una fecha, o una antigüedad como `30 days` o `6 months`. Vacío significa sin límite. Aproximado: nunca se deja fuera nada más nuevo, y unos pocos videos justo por encima del límite pueden colarse. | Para tomar solo los videos recientes de un canal. | `6 months` |
| **Only new videos since the last run** (`onlyNewVideos`) | Para ejecuciones programadas: cada ejecución entrega solo los videos que no había entregado antes para las mismas búsquedas, canales y listas. Desactivado por defecto. | Cuando lo ejecutas de forma programada. Mira «Ejecútalo de forma programada y recibe solo los videos nuevos». | `true` |
| **Memory name** (`stateKey`) | «Nombre de la memoria». Opcional, solo con la casilla anterior: el nombre de la lista de videos entregados. | Para mantener una sola lista aunque cambien tus búsquedas. | `my-weekly-topic` |
| **🎞️ Kinds of videos to take from a channel** (`contentTypes`) | «Tipos de videos del canal»: videos normales (`videos`), Shorts (`shorts`), grabaciones de directos (`streams`). Los tres por defecto. | Desmarca lo que no necesites. | `["videos"]` |

**⚙️ More options** («Más opciones»; los valores por defecto sirven para casi todo)

| Casilla | Qué es | Cuándo tocarla | Ejemplo |
| --- | --- | --- | --- |
| **🔁 Translate to** (`translateTo`) | «Traducir a»: la traducción automática de YouTube, que se usa cuando el video no tiene subtítulos en ese idioma. YouTube no la ofrece para todos los videos; entonces recibes el idioma original y una nota en `message`. | Quieres todo en un idioma. | `es` |
| **If my language is missing, give me the original language** (`fallbackToAnyLanguage`) | «Si falta mi idioma, dame el original». Activado: un video sin tus idiomas devuelve igualmente su transcripción en su idioma. Desactivado: esos videos se omiten, sin coste. | Otro idioma no te sirve. | `false` |
| **Caption type** (`captionType`) | «Tipo de subtítulos»: Any, human-made first (`any`); Human-made only (`manual`); Auto-generated only (`auto`). | `manual` cuando necesitas puntuación y nombres correctos. | `manual` |
| **Merge caption lines into blocks** (`mergeSegmentsSeconds`) | «Unir líneas en bloques»: junta las líneas cortas en bloques de unos tantos segundos. `0` conserva las líneas originales. Hasta 3600. | Para índices de búsqueda y fragmentos para IA. | `60` |
| **Include video details** (`includeMetadata`) | «Incluir datos del video»: añade canal, duración, visualizaciones, descripción, etiquetas y miniatura. El título va siempre. | Desactívalo para filas más pequeñas. | `false` |

#### Investigación de producto: elogios y quejas, análisis de 4 a 20 minutos

```json
{
    "searchQueries": ["espresso machine review", "espresso machine problems"],
    "maxVideosPerSearch": 10,
    "searchDuration": "medium",
    "outputFormats": ["text"]
}
```

#### Una noticia: los videos de esta semana, primero los más nuevos

```json
{
    "searchQueries": ["port strike"],
    "maxVideosPerSearch": 20,
    "searchUploadDate": "week",
    "searchSortBy": "date",
    "outputFormats": ["text"]
}
```

#### Lo que ya posiciona, primero los más vistos de los encontrados, con marcas de tiempo

```json
{
    "searchQueries": ["how to learn spanish"],
    "maxVideosPerSearch": 30,
    "searchSortBy": "views",
    "outputFormats": ["timestampedText"]
}
```

#### Una búsqueda en español, traducida al inglés

```json
{
    "searchQueries": ["cafetera superautomática opiniones"],
    "maxVideosPerSearch": 10,
    "translateTo": "en",
    "outputFormats": ["text"]
}
```

#### Una búsqueda más los videos de un canal de los últimos seis meses

```json
{
    "searchQueries": ["standing desk review"],
    "onlyWithCaptions": false,
    "urls": ["https://www.youtube.com/@TED"],
    "maxVideosPerSource": 20,
    "newerThan": "6 months",
    "contentTypes": ["videos"]
}
```

#### Vigilancia semanal de un tema, solo videos nuevos

```json
{
    "searchQueries": ["espresso machine review", "espresso machine problems"],
    "maxVideosPerSearch": 20,
    "searchUploadDate": "month",
    "onlyNewVideos": true,
    "stateKey": "espresso-weekly",
    "outputFormats": ["text"]
}
```

#### Cómo funciona de verdad el orden

- **Most relevant first** es el orden de YouTube. Los videos llegan clasificados como YouTube los clasifica para esa búsqueda.
- **Newest first** y **Most viewed first** no piden a YouTube otra lista. El Actor toma los mismos videos (los primeros resultados por relevancia, tantos como diga **🔢 Videos per search**) y los ordena por fecha o por visualizaciones.

Así que «Most viewed first» con 10 videos te da los 10 videos más relevantes con el más visto arriba, no los 10 videos más vistos de YouTube sobre ese tema. «Newest first» ordena por la antigüedad aproximada que muestra la lista de YouTube («hace 3 semanas»), así que dos videos con el mismo texto de antigüedad conservan entre ellos el orden de YouTube; la fecha exacta está en `publishedAt` en cada fila.

Dos reglas prácticas: ¿quieres videos recientes? Pon **📅 Uploaded** en This week o This month, que de verdad estrecha la búsqueda, y añade **Newest first** si quieres. ¿Quieres una lista de «más vistos» más justa? Sube **🔢 Videos per search**: cuantos más videos se encuentran, más hay que ordenar.

#### Desde dónde se hace la búsqueda

La búsqueda se hace como YouTube responde a un visitante de Estados Unidos que usa YouTube en inglés y no ha iniciado sesión. Por eso tu propia página de YouTube puede mostrar una lista algo distinta, porque YouTube adapta los resultados a cada visitante. Los videos pueden estar en cualquier idioma: `cafetera superautomática opiniones` encuentra videos en español, y cada transcripción llega en el idioma de su video. La antigüedad aproximada de `publishedText` está en inglés ("3 weeks ago"). No hay ningún ajuste para elegir otro país.

### Ejecútalo de forma programada y recibe solo los videos nuevos

Una creadora de contenido quiere una hoja que se rellene sola cada lunes con lo nuevo de su tema. Así se hace.

#### Cómo funciona «Only new videos since the last run»

- Activa **Only new videos since the last run** (`onlyNewVideos`). Está desactivado por defecto y está pensado para ejecuciones programadas (las **Schedules** de Apify, «Programaciones»).
- Cada ejecución entrega entonces solo los videos que no había entregado antes para las mismas búsquedas, canales y listas. Los videos ya entregados se dejan fuera **antes de pedir nada**, así que ni se piden ni se cobran.
- La primera ejecución entrega todo lo que encuentra y lo recuerda.
- Los enlaces de videos que pegas directamente se entregan siempre, nunca se dejan fuera. Se recuerdan, así que una búsqueda o un canal que los encuentre más adelante no los vuelve a entregar.

#### Configúralo paso a paso

1. **Rellena el formulario:** tus búsquedas, **🔢 Videos per search**, los filtros (por ejemplo **📅 Uploaded**: This month) y los formatos. Marca **Only new videos since the last run**. Opcional: escribe un **Memory name** como `espresso-weekly`.
2. **Haz clic en Start una vez a mano.** La primera ejecución entrega lo que encuentra y lo recuerda. Además deja el Actor listo para programarse: un Actor tiene que haberse ejecutado una vez antes de poder programarlo.
3. **Haz clic en Save as a new task** («Guardar como tarea nueva»), arriba a la derecha en la página del Actor. Una **tarea** es un formulario guardado; aparece en **Saved tasks** («Tareas guardadas»).
4. **Abre Schedules** en el menú de la izquierda y haz clic en **Create new** («Crear nueva»).
5. **En la tarjeta Schedule setup** («Configuración de la programación») elige cada cuánto (una expresión cron como `@weekly`, o el constructor visual) y tu zona horaria. **Next runs** («Próximas ejecuciones») muestra las fechas siguientes.
6. **En el desplegable Add** («Añadir») elige tu tarea.
7. **Haz clic en Enable** («Activar»). Las programaciones nuevas empiezan desactivadas.

#### Qué verás en las ejecuciones siguientes

- El registro y el mensaje final de la ejecución dicen cuántos videos se dejaron fuera, por ejemplo: "Skipped 37 videos already delivered in earlier runs (memory 'x')."
- Una ejecución sin nada nuevo termina con normalidad, sin filas: "No new videos since the last run: … Nothing was charged."

#### Nombre de la memoria: una lista o varias

- **Vacío:** la memoria se llama `auto-…` según tu lista de búsquedas, canales y listas. El orden y las mayúsculas de las búsquedas no importan; cualquier otro cambio en la lista empieza una memoria nueva.
- **Con un nombre:** eliges tú el registro. El mismo nombre mantiene una sola lista de videos entregados aunque cambien las búsquedas o los enlaces; un nombre distinto mantiene una lista aparte.
- Se conservan las letras, las cifras y `-_.!'()`; los demás caracteres se convierten en `-`.

#### Dónde se guarda la memoria y cómo empezar de cero

La memoria se guarda en tu propia cuenta de Apify: **Storage** > **Key-value stores** («almacenes de archivos»), en el almacén llamado `youtube-transcripts-memory`. Cada memoria es un registro que guarda los identificadores de los 20 000 videos entregados más recientes y la hora de su última actualización. Borra el registro, o el almacén entero, y la siguiente ejecución empieza de cero y vuelve a entregarlo todo.

#### Conviene saber

- **Se recuerdan:** los videos entregados como `success` o `no_captions`. Todo lo demás (`blocked`, `error`, `age_restricted`, un directo que sigue en marcha o no ha empezado) se vuelve a intentar en la siguiente ejecución.
- **Qué cuenta como nuevo:** cada ejecución vuelve a buscar y mira los primeros **🔢 Videos per search** resultados; los entregados antes se dejan fuera. Un video que entra por primera vez entre los primeros resultados es nuevo, aunque sea antiguo.
- **La memoria se guarda al final de la ejecución,** también cuando la ejecución se detiene antes de su tiempo máximo o en el tope de gasto (entonces, solo con los videos que se procesaron). Tras un traslado de servidor, la ejecución continúa y guarda al final; nada se cuenta ni se cobra dos veces.
- **Si la memoria no se puede leer,** la ejecución anota un aviso y entrega todos los videos como una ejecución normal. Si no se puede guardar, los resultados se entregan igualmente, y la siguiente ejecución puede repetir algunos videos.
- **Dos ejecuciones con la misma memoria al mismo tiempo** pueden entregar las dos el mismo video nuevo. La memoria guarda los videos de ambas.

#### Envía los videos nuevos a Google Sheets o Slack

- **n8n:** añade el nodo **Apify Trigger** (**Resource to Watch**: **Task**, tu tarea; **Event Type**: **Succeeded**). Después un nodo **Apify** con Resource **Dataset**, **Get Items** y el `defaultDatasetId` de la ejecución como **Dataset ID** (sube **Limit**: empieza en 50). Después un nodo de Google Sheets que añada una fila por elemento (`sourceTitle`, `title`, `channelName`, `viewCount`, `url`), o un paso de IA que resuma `text` y un nodo de Slack que publique el resumen. Un n8n en tu propio servidor necesita una `WEBHOOK_URL` pública para que Apify pueda llegar a él.
- **Make:** empieza con **Watch Actor Runs** («Vigilar ejecuciones del Actor»; se dispara cuando termina una ejecución), después **Get Dataset Items** y después un módulo de Google Sheets (la guía de Apify usa "Bulk Add Rows"), o un paso de resumen y un módulo de Slack.
- **Cualquier otra herramienta:** en la pestaña **Integrations** («Integraciones») de la tarea añade un webhook («aviso automático a otra aplicación») para el evento `ACTOR.RUN.SUCCEEDED`. Apify envía un POST a tu dirección; el identificador del dataset está en `resource.defaultDatasetId`. El receptor debe responder en menos de 2 minutos, y un webhook puede llegar dos veces.

Una ejecución sin videos nuevos termina igualmente como Succeeded, con un dataset vacío: esa semana tu hoja simplemente no recibe filas nuevas.

### Usa YouTube Search Transcripts Scraper desde agentes de IA (MCP)

MCP es un estándar que permite a un asistente de IA usar herramientas externas. Esta dirección le da al asistente este Actor como herramienta:

```text
https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts
```

El inicio de sesión es OAuth: en la primera conexión se abre una ventana del navegador y no se guarda ningún token en la configuración. También sirve una cabecera `Authorization: Bearer <YOUR_APIFY_TOKEN>`. El asistente inicia una ejecución, espera (hasta 45 segundos por llamada) y después lee las filas. Mantén pequeñas las búsquedas del asistente, de 5 a 10 videos. Cada ejecución se paga con tu plan de Apify como cualquier otra.

#### Claude.ai y Claude Desktop

1. Abre la página de conectores de Claude (**Settings** > **Connectors**; en algunas versiones **Customize** > **Connectors**; «Configuración» o «Personalizar», y «Conectores»).
2. Elige **Add custom connector** («Añadir conector personalizado»).
3. Rellena **Name** («Nombre»; por ejemplo "YouTube search") y **Remote MCP server URL** («URL del servidor MCP remoto») con la dirección de arriba. Haz clic en **Continue** («Continuar») y luego en **Add** («Añadir»).
4. Inicia sesión en Apify cuando el navegador te lo pida.
5. En un chat, haz clic en **+**, luego en **Connectors**, y activa el conector.

En el plan gratuito de Claude puedes añadir un conector personalizado. En los planes Team y Enterprise lo añade un propietario («Owner») para toda la organización.

#### Claude Desktop con un servidor local

`claude_desktop_config.json`, necesita Node.js:

```json
{
  "mcpServers": {
    "youtube-search-transcripts": {
      "command": "npx",
      "args": ["-y", "@apify/actors-mcp-server", "--tools", "nokia2k/youtube-search-transcripts"],
      "env": {
        "APIFY_TOKEN": "<YOUR_APIFY_TOKEN>"
      }
    }
  }
}
```

#### Cursor

`.cursor/mcp.json` en tu proyecto, o `~/.cursor/mcp.json`:

```json
{
  "mcpServers": {
    "youtube-search-transcripts": {
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts"
    }
  }
}
```

Con un token en lugar de OAuth, añade `"headers": { "Authorization": "Bearer <YOUR_APIFY_TOKEN>" }` junto a `"url"`.

#### VS Code

Necesita GitHub Copilot. Abre la paleta de comandos, ejecuta **MCP: Open User Configuration** («abrir la configuración de usuario de MCP») y añade:

```json
{
  "servers": {
    "youtube-search-transcripts": {
      "type": "http",
      "url": "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts"
    }
  }
}
```

#### Claude Code y ChatGPT

```bash
claude mcp add --transport http youtube-search-transcripts \
  "https://mcp.apify.com?tools=nokia2k/youtube-search-transcripts" \
  --header "Authorization: Bearer $APIFY_TOKEN"
```

**ChatGPT:** tiene que estar activado el modo Developer («modo de desarrollador»). Ve a **Settings > Apps & Connectors > Create** («Crear»), rellena **Name**, **Description** («Descripción») y **MCP Server URL** con la dirección de arriba, deja **Authentication** («Autenticación») en OAuth y haz clic en **Create**. En un chat, haz clic en **+**, luego en **More** («Más»), y elige el conector.

#### Ejemplos de peticiones

- «Busca en YouTube "standing desk review", toma los 5 primeros videos y dime las tres quejas que repiten los analistas.»
- «Encuentra los 10 mejores videos de este mes sobre "n8n automation tutorial" y enumera las herramientas que usa cada uno.»
- «Busca "how to learn spanish", lee los 5 videos más relevantes y escribe un plan de estudio con aquello en lo que coinciden.»

También puedes subir sin más `ALL_TRANSCRIPTS.md` a cualquier asistente.

### Integraciones: n8n, Make, Zapier, Python, JavaScript y LangChain

Para casi todas necesitas tu **API token** («token de API»): una clave secreta que permite a otro programa usar tu cuenta de Apify. Está en Apify Console, en **Settings** («Configuración»), **API & Integrations** («API e integraciones»). Trátalo como una contraseña. En los ejemplos se escribe `<YOUR_APIFY_TOKEN>`.

#### API: una llamada con curl

En las direcciones web el Actor se escribe `nokia2k~youtube-search-transcripts` (con tilde); en las bibliotecas cliente, `nokia2k/youtube-search-transcripts`. Esta llamada inicia una ejecución, espera y devuelve las filas. Espera hasta 300 segundos; una ejecución más larga responde HTTP 408.

```bash
curl -X POST "https://api.apify.com/v2/acts/nokia2k~youtube-search-transcripts/run-sync-get-dataset-items" \
  -H "Authorization: Bearer <YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["espresso machine review"], "maxVideosPerSearch": 5, "outputFormats": ["text"]}'
```

El flujo en tres pasos para ejecuciones largas:

1. `POST https://api.apify.com/v2/acts/nokia2k~youtube-search-transcripts/runs` con la entrada en JSON. La respuesta trae el `id` de la ejecución y su `defaultDatasetId`. Añade `?waitForFinish=60` para esperar hasta 60 segundos.
2. `GET https://api.apify.com/v2/actor-runs/<RUN_ID>` hasta que `status` sea `SUCCEEDED`.
3. `GET https://api.apify.com/v2/datasets/<DATASET_ID>/items?format=json&clean=true`.

Para poner un tope al gasto desde el código, añade el parámetro `maxTotalChargeUsd` (en Python, `max_total_charge_usd`).

#### Python

`pip install apify-client` (versión 3, Python 3.11 o posterior):

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("nokia2k/youtube-search-transcripts").call(
    run_input={
        "searchQueries": ["espresso machine review", "espresso machine problems"],
        "maxVideosPerSearch": 10,
        "outputFormats": ["text"],
    },
)
rows = list(client.dataset(run.default_dataset_id).iterate_items())
rows.sort(
    key=lambda row: (row.get("sourceTitle") or "", row.get("positionInSource") or 0)
)
for row in rows:
    if row["status"] == "success":
        print(
            row["sourceTitle"],
            row["positionInSource"],
            row["title"],
            row["wordCount"],
            "words",
        )
    else:
        print(row["input"], "->", row["status"], row["message"])
```

#### JavaScript

`npm install apify-client`:

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

const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });
const run = await client.actor('nokia2k/youtube-search-transcripts').call({
    searchQueries: ['how to learn spanish'],
    maxVideosPerSearch: 5,
    searchUploadDate: 'month',
    onlyWithCaptions: true,
    outputFormats: ['text'],
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items.filter((r) => r.status === 'success')) {
    console.log(row.positionInSource, row.title, row.channelName, row.url);
}
```

#### LangChain

`pip install langchain-apify langchain-core` y define `APIFY_API_TOKEN` (la variable que lee `langchain-apify`):

```python
from langchain_apify import ApifyWrapper
from langchain_core.documents import Document

loader = ApifyWrapper().call_actor(
    actor_id="nokia2k/youtube-search-transcripts",
    run_input={
        "searchQueries": ["espresso machine review"],
        "maxVideosPerSearch": 10,
        "outputFormats": ["text"],
    },
    dataset_mapping_function=lambda row: Document(
        page_content=row.get("text") or "",
        metadata={
            "source": row.get("url"),
            "title": row.get("title"),
            "query": row.get("sourceTitle"),
            "status": row.get("status"),
        },
    ),
)
docs = [d for d in loader.load() if d.metadata["status"] == "success"]
```

#### n8n

En n8n una automatización es un «flujo de trabajo» y cada paso, un «nodo».

1. Instala el nodo de Apify. En n8n Cloud: abre el lienzo, abre el panel de nodos y busca **Apify**; aparece en **More from the community** («Más de la comunidad»); instálalo. En tu propio servidor: **Settings > Community Nodes**, **Install**, escribe `@apify/n8n-nodes-apify`. Necesita n8n 1.57.0 o posterior.
2. Añade el nodo **Apify**. Pon Resource («Recurso») en **Actor** y Operation («Operación») en **Run an Actor and Get Dataset** («Ejecutar un Actor y obtener los resultados»).
3. Crea la credencial **Apify API** y pega tu token en **API Key**. En n8n Cloud también puedes usar **Apify OAuth2 API** y **Connect my account** («Conectar mi cuenta»).
4. En **Actor**, elige YouTube Search Transcripts Scraper.
5. En **Input JSON**, pega `{"searchQueries": ["espresso machine review"], "maxVideosPerSearch": 5, "outputFormats": ["text"]}`. Para buscar un valor de un nodo anterior, pon una expresión de n8n en lugar de las palabras.
6. Pon **Memory** («Memoria») en 4096, el valor por defecto de este Actor. El nodo envía 1024 si no lo cambias.
7. Opcional: rellena **Maximum Cost per Run (USD)** («Coste máximo por ejecución»).
8. Ejecuta el nodo.
   - *Verás:* un elemento de n8n por video. Conecta el nodo siguiente, por ejemplo uno que resuma `text`.

Si la salida parece rara o vacía, seguramente elegiste **Run an Actor**, que devuelve la ejecución y no las transcripciones. Usa **Run an Actor and Get Dataset**, o añade **Get Items** con el `defaultDatasetId` del nodo anterior como **Dataset ID**. **Get Items** devuelve 50 filas si no subes **Limit** («Límite»).

#### Make

En Make una automatización es un «escenario» y cada paso, un «módulo».

1. Crea un escenario y añade el módulo de Apify **Run an Actor** («Ejecutar un Actor»).
2. Crea la **Connection** («Conexión»): inicia sesión en Apify o pega tu token en el campo **API token**.
3. Elige el **Actor**, pon **Run synchronously** («Ejecutar de forma síncrona») en **Yes** y pega el mismo JSON en **Input JSON**.
4. Añade el módulo **Get Dataset Items** («Obtener los resultados»). En **Dataset ID**, asigna el identificador del dataset por defecto (`defaultDatasetId`) del primer módulo. Si no aparece, ejecuta el escenario una vez y vuelve a mirar. Sube **Limit** si esperas muchas filas.
5. Añade el destino, por ejemplo un módulo de Google Sheets.

Las ejecuciones síncronas en Make son para trabajos cortos. Para ejecuciones de más de unos 2 minutos, usa **Watch Actor Runs** («Vigilar ejecuciones del Actor»; se dispara cuando termina una ejecución) y después **Get Dataset Items**.

#### Zapier

Zapier espera como máximo 30 segundos por una ejecución síncrona y después pierde la salida. Una búsqueda pequeña puede caber, pero lo seguro son dos Zaps:

1. Zap A: tu disparador (una programación, una fila nueva en una hoja) y después la acción de Apify **Run Actor**, configurada como asíncrona. Conecta tu cuenta con **Allow Access** («Permitir acceso»).
2. Zap B: el disparador de Apify **Finished Actor Run** («Ejecución del Actor terminada»), después **Fetch Dataset Items** («Traer los resultados») y después tu destino.
3. Comprueba cada paso con **Test Trigger** («Probar el disparador») y **Test step** («Probar el paso»).

#### Google Sheets y Excel

- **Excel:** en la pestaña **Output** de la ejecución haz clic en **Export**, elige Excel, haz clic en **Download** y abre el archivo.
- **Google Sheets a mano:** exporta como CSV o Excel de la misma forma e importa el archivo en Google Sheets.
- **Una hoja ordenada:** en la ventana de exportación, rellena **Select fields** («Elegir campos») con las columnas que quieres en el orden que quieres, por ejemplo `sourceTitle`, `positionInSource`, `title`, `channelName`, `viewCount`, `likeCount`, `publishedAt`, `url`, `text`.
- **Google Drive automáticamente:** en la pestaña **Integrations** del Actor elige **Upload results to GDrive**, haz clic en **Connect with Google**, rellena **Filename** («nombre del archivo») y **Format** («formato») y haz clic en **Save** («Guardar»). El archivo llega a la carpeta `Apify Uploads` después de cada ejecución correcta.
- **Google Sheets automáticamente:** usa Make, Zapier o n8n como arriba, con su paso de Google Sheets a

# Actor input Schema

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

Type what you would type into YouTube's search box, one search per line. For each search you get the transcripts of the videos YouTube ranks highest. No links needed.

## `maxVideosPerSearch` (type: `integer`):

How many of the top results to take for each search. You only pay for transcripts that are delivered.

## `urls` (type: `array`):

Optional. Besides searching, you can paste channel, playlist or video links here. Paste YouTube links, one per line. A <b>channel</b> link (<code>https://www.youtube.com/@name</code>) gives you the videos of the channel, newest first. A <b>playlist</b> link gives you the videos of the playlist. A <b>video</b> link gives you that one video. You can mix them freely, and a plain <code>@name</code> works too.

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

How many videos to take from each channel or playlist. Start with a small number to see what you get, then raise it. You only pay for transcripts that are delivered.

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

Leave it on <b>Original language</b> to get each video in the language it is spoken in: you do not need to know the language in advance. Or pick one or more languages in order of preference, and the first one the video has is used. You can also type any language code (<code>pt-BR</code>, <code>es-419</code>) or a language name (<code>Spanish</code>, <code>español</code>, <code>German</code>). Captions written by people are preferred over auto-generated ones. Every row tells you which language was returned and which other languages the video has.

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

Choose how each transcript is returned. You can pick several: the price per transcript stays the same.

## `combinedFile` (type: `boolean`):

Saves all transcripts of the run into one file, <code>ALL_TRANSCRIPTS.md</code>, with a heading, the channel, the date and the link for every video. Upload it to ChatGPT, Claude, NotebookLM or your knowledge base in one go. You find it in the run's <b>Storage > Key-value store</b> tab. Large runs are split into files of about 8 MB. The price stays the same.

## `saveFiles` (type: `boolean`):

Saves a ready-to-use file per video for each text format you selected above: <code>.srt</code>, <code>.vtt</code> and <code>.txt</code>. Each row gets a <code>files</code> field with direct download links, and all files are listed in the run's <b>Storage > Key-value store</b> tab. The price per transcript stays the same.

## `searchUploadDate` (type: `string`):

Keep only videos uploaded recently. This is YouTube's own filter.

## `searchDuration` (type: `string`):

Keep only short, medium or long videos. This is YouTube's own filter.

## `searchSortBy` (type: `string`):

Relevance is what YouTube shows by default. The other two put the found videos in order of date or views.

## `onlyWithCaptions` (type: `boolean`):

Asks YouTube for videos with captions only, so nearly every result comes back with a transcript.

## `newerThan` (type: `string`):

Leave empty for no limit. Pick a date, or an age such as <code>30 days</code> or <code>6 months</code>. YouTube's lists show the age of a video only roughly ("3 weeks ago"), so the cut is approximate: nothing newer is ever left out, and a few videos just past the limit can slip in. Every row carries the exact date in <code>publishedAt</code>.

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

Turn this on when you run the Actor on a <b>schedule</b> (for example every day). Each run then gives you only the videos it has not delivered before for the same channels, playlists and searches, so you never get, or pay for, the same video twice. The first run delivers everything it finds and remembers it. The list of delivered videos is kept in your own Apify account, in the key-value store named <code>youtube-transcripts-memory</code> (<b>Storage > Key-value stores</b>). Videos that failed are tried again next time. Video links you paste directly are always delivered.

## `stateKey` (type: `string`):

Optional. Used only with <b>Only new videos since the last run</b>. Leave it empty and the Actor makes a name from your channels, playlists and searches, so every combination has its own list. Type a name (for example <code>my-daily-news</code>) to choose it yourself: the same name keeps adding to the same list even if you change the links, and a different name starts a separate list. Letters, digits and <code>-\_.!'()</code>; other characters become <code>-</code>.

## `contentTypes` (type: `array`):

A channel can hold regular videos, Shorts and recordings of live streams. Untick what you do not need.

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

Pick a language to get YouTube's own machine translation when the video has no captions in that language. If the video already has captions in it, those are returned instead, because they are better than a machine translation. YouTube does not offer translation for every video: when it does not, you get the original language and a note in <code>message</code>.

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

When on, a video without captions in the languages you picked still returns its transcript in its original language, and the <code>language</code> and <code>message</code> fields tell you which one. Turn it off to skip those videos instead. Skipped videos are not charged.

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

Choose which captions are accepted. Auto-generated captions are produced by YouTube's speech recognition and have no punctuation in many languages.

## `mergeSegmentsSeconds` (type: `integer`):

YouTube captions come in lines of a few words. Set a number of seconds, for example 30, to join them into longer blocks. This is useful for chapters, search indexes and RAG chunks. Use 0 to keep the original lines.

## `includeMetadata` (type: `boolean`):

Adds channel, duration, view count, description, keywords and thumbnail to every row. The video title is always included.

## Actor input object example

```json
{
  "searchQueries": [
    "how to learn spanish",
    "iphone 17 review"
  ],
  "maxVideosPerSearch": 5,
  "urls": [
    "https://www.youtube.com/@TED",
    "https://www.youtube.com/watch?v=arj7oStGLkU"
  ],
  "maxVideosPerSource": 5,
  "languages": [
    "es",
    "en"
  ],
  "outputFormats": [
    "text",
    "markdown"
  ],
  "combinedFile": true,
  "saveFiles": false,
  "searchUploadDate": "any",
  "searchDuration": "any",
  "searchSortBy": "relevance",
  "onlyWithCaptions": true,
  "newerThan": "30 days",
  "onlyNewVideos": false,
  "stateKey": "my-daily-news",
  "contentTypes": [
    "videos"
  ],
  "translateTo": "es",
  "fallbackToAnyLanguage": true,
  "captionType": "any",
  "mergeSegmentsSeconds": 0,
  "includeMetadata": true
}
```

# Actor output Schema

## `transcripts` (type: `string`):

All results of the run. Each item has a status field: only items with status 'success' contain a transcript and were charged.

# 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 = {
    "searchQueries": [
        "how to learn spanish"
    ],
    "maxVideosPerSearch": 5,
    "maxVideosPerSource": 5
};

// Run the Actor and wait for it to finish
const run = await client.actor("nokia2k/youtube-search-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 = {
    "searchQueries": ["how to learn spanish"],
    "maxVideosPerSearch": 5,
    "maxVideosPerSource": 5,
}

# Run the Actor and wait for it to finish
run = client.actor("nokia2k/youtube-search-transcripts").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 '{
  "searchQueries": [
    "how to learn spanish"
  ],
  "maxVideosPerSearch": 5,
  "maxVideosPerSource": 5
}' |
apify call nokia2k/youtube-search-transcripts --silent --output-dataset

```

## MCP server setup

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

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/yvyR1s53TpK3auW8u/builds/44GiKgzYQIoCb3wzg/openapi.json
