# YouTube Transcript Scraper: YouTube Captions & Subtitles (`xmolodtsov/youtube-transcript-scraper`) Actor

Get YouTube transcripts, subtitles and captions as text with timestamps from video, Shorts, channel or playlist URLs. Pick languages, manual or auto-generated captions. Includes title, channel, views, likes and publish date. Pay only per transcript; videos without captions are free.

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

## Pricing

from $3.00 / 1,000 transcripts

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

![YouTube Transcript Scraper: YouTube Captions & Subtitles logo](https://api.apify.com/v2/key-value-stores/2DxCOvbMjhvK3S1qP/records/youtube-transcript-scraper-avatar-128)

### What does YouTube Transcript Scraper do?

YouTube Transcript Scraper downloads YouTube captions and subtitles as clean text with timestamps from any public YouTube video that has captions, for AI and LLM pipelines, content repurposing and research. It reads human-made or auto-generated captions in the languages you choose. Paste video, Shorts, channel or playlist URLs and get one row per video: the full transcript text, timestamped segments, the caption language, and video metadata such as title, channel, views, likes, duration and publish date.

You pay only for transcripts returned. Videos without captions, unavailable videos and errors are free.

Who uses it:

- **AI and LLM builders** feed transcript text into prompts, RAG pipelines, embeddings and search indexes.
- **Content teams** repurpose videos into blog posts, newsletters, show notes and social posts.
- **Researchers and analysts** study what is said across hundreds of videos, channels or playlists.
- **SEO specialists** publish text versions of videos and learn the words a niche uses.
- **Accessibility teams** produce readable text for people who can't watch or hear a video.

Highlights:

- **Bulk transcripts from videos, Shorts, channels and playlists** in one input. A channel URL expands to its latest uploads, a playlist to its videos.
- **Language control**: list your preferred caption languages in order, choose manual or auto-generated captions, and fall back to the video's own language.
- **No setup**: a residential proxy is built in and included in the price, so requests don't come from the datacenter IPs YouTube blocks. No YouTube login, API key or cookies.
- **Plain HTTP, no browser**: in our tests, 200 videos took about 95 seconds.
- **Clear outcomes**: every video gets a row; videos without a transcript come back free, with a machine-readable reason.

### What data you get

One dataset item per video.

| Field | Example | Description |
|---|---|---|
| `video_id`, `url` | `F0OkwXKcPSE` | Video ID and watch URL |
| `title` | `Hi Me In 10 Years` | Video title |
| `channel_id`, `channel_name`, `channel_username` | `MrBeast`, `@MrBeast` | Channel ID, name and @handle |
| `view_count`, `like_count` | `69706457`, `3832915` | Views and likes |
| `duration_seconds` | `194` | Video length in seconds |
| `published_at`, `timestamp` | `2025-10-04T09:00:07-07:00` | Publish date, also as a Unix timestamp |
| `category` | `Entertainment` | YouTube category |
| `description`, `keywords` | `["Mr.Beast", "mr", "beast"]` | Description text and video tags |
| `thumbnail` | `https://i.ytimg.com/vi/…/maxresdefault.jpg` | Largest thumbnail URL |
| `language`, `selected_language` | `en`, `English` | Code and name of the returned caption track |
| `available_languages` | `["English", "French", …]` | Every caption track the video has |
| `is_auto_generated` | `false` | `true` when YouTube's speech recognition made the captions |
| `transcript` | `[{"text": "…", "start": 0.292, "end": 1.542, "duration": 1.25}]` | Timestamped segments, in seconds |
| `transcript_text` | `Hi, me in ten years. …` | The full transcript as one string |
| `status`, `error_code`, `message` | `success`, `null` | Outcome, and a reason code when there is no transcript |
| `input` | `https://www.youtube.com/@MrBeast` | The input line that produced the row |

Likes, publish date, category and @handle are filled when YouTube includes them, which in our tests was almost every video; otherwise they are `null`, never guessed.

### How to use YouTube Transcript Scraper

1. Open the Actor in Apify Console and go to the **Input** tab.
2. Paste video, Shorts, channel or playlist URLs (or 11-character video IDs) into **YouTube URLs or video IDs**, one per line.
3. Pick your **Preferred languages** and **Caption type**, and set **Max videos per channel or playlist** if you pasted channels or playlists.
4. Click **Start**. Download the transcripts from the **Output** tab as JSON, CSV, Excel or HTML, or read them through the API.

#### Use it as a YouTube transcript API

This call runs the Actor and returns the transcripts. Synchronous calls time out after 5 minutes, so for large batches start a run and read its dataset afterwards.

```bash
curl -X POST "https://api.apify.com/v2/acts/xmolodtsov~youtube-transcript-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"], "languages": ["en"]}'
```

With the Apify Python client:

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("xmolodtsov/youtube-transcript-scraper").call(
    run_input={"urls": ["https://www.youtube.com/@NASA"], "maxVideosPerChannel": 20}
)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item["status"], (item["transcript_text"] or "")[:80])
```

#### Use it from an AI agent (MCP)

Connect your MCP client to `https://mcp.apify.com?tools=xmolodtsov/youtube-transcript-scraper` and the agent can fetch YouTube transcripts as a tool call. The usual pricing and free-plan limits apply.

### Pricing

Pay per event, all-inclusive: you are charged per transcript returned, and the proxy and platform usage are included. There are no start fees and no separate compute bill.

| Apify plan | Price per transcript | Price per 1,000 transcripts |
|---|---|---|
| Free | $0.005 | $5.00 |
| Starter (Bronze) | $0.004 | $4.00 |
| Scale (Silver) | $0.0035 | $3.50 |
| Business (Gold) | $0.003 | $3.00 |

Platinum and Diamond plans pay the Gold price. Rows without a transcript (`not_available` and `error`) are never charged.

#### How much does it cost to scrape YouTube transcripts?

One transcript is one charge, whatever the video's length: a 30-second Short and a three-hour podcast cost the same.

Example: you paste five channel URLs and set **Max videos per channel or playlist** to 200. That queues 1,000 videos. 950 have captions and 50 don't. You pay for 950 transcripts: $3.80 on Starter, $2.85 on Business. The 50 rows without captions are free. Duplicate videos are removed across the whole run, so a video that appears in two playlists is fetched and charged once.

To cap spending, set a maximum cost per run in Console or the API. The Actor stops cleanly at that limit and keeps every transcript fetched so far.

#### Is it free?

You can try it on the free Apify plan. Free-plan runs are limited to **10 videos per run** and **5 runs per calendar month per account**. These limits are set by the developer of this Actor, not by Apify, and any paid Apify plan removes them. A capped run still succeeds and says so in its status message. Once the five runs are used, further runs end successfully with an explanation and no results until the 1st of the next month.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `urls` | array of strings | required (or a drop-in field, below) | Video URLs (`watch?v=`, `youtu.be/`, `/shorts/`, `/live/`, `/embed/`), 11-character video IDs, channel URLs or @handles, and playlist URLs, mixed freely. |
| `maxVideosPerChannel` | integer | `10` | Latest videos taken from each channel or playlist (1 to 5,000). Single video URLs are not affected. |
| `languages` | array of strings | `["en"]` | Caption language codes in order of preference. `en` also matches `en-GB` and other regional variants. |
| `captionType` | string | `any` | `any` (human-made first, auto-generated otherwise), `manual` or `auto`. |
| `fallbackToAnyLanguage` | boolean | `true` | When none of your languages exists, return the video's original language instead of a free `language_not_found` row. |
| `includeTimestamps` | boolean | `true` | Include the timestamped `transcript` segments. `transcript_text` is always included. |
| `proxyConfiguration` | object | built in | Residential proxy is built in and included. Change it only to use your own proxies. |

Example input:

```json
{
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "F0OkwXKcPSE",
        "https://www.youtube.com/@NASA",
        "https://www.youtube.com/playlist?list=<playlist-id>"
    ],
    "maxVideosPerChannel": 20,
    "languages": ["en", "es", "de"],
    "captionType": "any",
    "fallbackToAnyLanguage": true,
    "includeTimestamps": true
}
```

**Drop-in field names.** The Actor also reads `youtube_url`, `channel_url`, `videoUrl`, `startUrls`, `max_videos` and `language`, so a saved input from another YouTube transcript tool runs as is.

### Output

Each video is one item in the dataset. A success row, with the segments and long lists shortened:

```json
{
    "video_id": "F0OkwXKcPSE",
    "url": "https://www.youtube.com/watch?v=F0OkwXKcPSE",
    "title": "Hi Me In 10 Years",
    "channel_id": "UCX6OQ3DkcsbYNE6H8uQQuVA",
    "channel_name": "MrBeast",
    "channel_username": "@MrBeast",
    "view_count": 69706457,
    "like_count": 3832915,
    "duration_seconds": 194,
    "published_at": "2025-10-04T09:00:07-07:00",
    "timestamp": 1759593607,
    "category": "Entertainment",
    "description": "Holy crap I will probably be so different when this goes public. …",
    "keywords": ["Mr.Beast", "mr", "beast"],
    "thumbnail": "https://i.ytimg.com/vi/F0OkwXKcPSE/maxresdefault.jpg",
    "language": "en",
    "selected_language": "English",
    "available_languages": ["Arabic", "English", "English (auto-generated)", "French", "German", "Spanish"],
    "is_auto_generated": false,
    "transcript": [
        { "text": "Hi, me in ten years.", "start": 0.292, "end": 1.542, "duration": 1.25 },
        { "text": "I'm gonna schedule upload this video ten years in the future.", "start": 2.25, "end": 5.834, "duration": 3.584 }
    ],
    "transcript_text": "Hi, me in ten years. I'm gonna schedule upload this video ten years in the future. So, you're gonna see this in 2025. …",
    "status": "success",
    "error_code": null,
    "message": "Transcript fetched (124 segments, English).",
    "input": "https://www.youtube.com/@MrBeast"
}
```

The **Overview** view in the Output tab shows the key columns. This preview is from a real run on a channel's latest uploads, with one free `not_available` row:

![YouTube Transcript Scraper output: transcripts with title, channel, language and status](https://api.apify.com/v2/key-value-stores/2DxCOvbMjhvK3S1qP/records/youtube-transcript-scraper-output.png)

### Guarantees and failure semantics

**Every video gets a row.** A video without a transcript still returns a row with `status: "not_available"` and an `error_code`. Title, channel and views are filled when YouTube returns them. These rows are free and never fail the run; they describe your data.

| `error_code` | What it means |
|---|---|
| `no_captions` | The video has no captions: the creator turned them off, or YouTube never made any. |
| `language_not_found` | Captions exist, but not in your languages or caption type. |
| `private`, `members_only`, `age_restricted` | The video needs a sign-in or membership. |
| `removed`, `not_found` | The video was removed, or no video has this ID. |
| `upcoming`, `live_now` | A premiere or stream that hasn't started, or one that is live now. Captions appear after it ends. |
| `live_recording_unavailable`, `geo_blocked`, `unplayable` | YouTube does not serve the video. |
| `channel_not_found`, `playlist_not_found`, `invalid_input` | YouTube doesn't know the channel or playlist, or the line isn't a YouTube URL or video ID. |

**Errors are retried, then reported.** Each video gets several attempts on different YouTube clients and fresh IP addresses. A video that still fails returns a free row with `status: "error"` and a code such as `blocked`, `rate_limited`, `timeout` or `network`.

**When a run fails.** A run is marked **FAILED**, so your integration can retry the batch, when:

- no transcript was returned and at least one video errored, or
- more than 20% of the requested videos, and more than two, ended in `error` rows, or
- the input contains no valid YouTube URL or video ID.

The status message starts with a reason code (for example `blocked:`), followed by a summary of transcripts, unavailable videos and errors. A run where every video is simply unavailable ends **SUCCEEDED**. Stopping at your maximum cost per run is a normal **SUCCEEDED** run.

**Run statistics.** The run's key-value store holds a `STATISTICS` record with counts per outcome and reason.

In our platform tests on 3 Oct 2026, 1,356 of 1,356 captioned videos returned a transcript.

### Integrations

- **Apify API and clients**: start runs and read results from any language, or with the JavaScript and Python clients.
- **Make, Zapier and n8n**: run the Actor from a workflow and pass transcripts to the next step.
- **LangChain and LlamaIndex**: load transcripts as documents for RAG and agents through Apify's integrations.
- **Google Sheets, Slack and webhooks**: send results where your team works, or trigger your own endpoint when a run finishes.
- **Schedules**: fetch a channel's newest videos every day or week.

### More scrapers from this developer

**TikTok:** [TikTok Search Scraper](https://apify.com/xmolodtsov/tiktok-search-scraper): videos and creators for any keyword, pay per result · [TikTok Profile Scraper (Pay Per Result)](https://apify.com/xmolodtsov/tiktok-profile-scraper): profiles, followers and latest posts by username · [TikTok Comments Scraper](https://apify.com/xmolodtsov/tiktok-comments-scraper): comments and full reply threads from any video · [TikTok Hashtag Scraper](https://apify.com/xmolodtsov/tiktok-hashtag-scraper): videos for any hashtag, with views, likes and music · [TikTok Sound & Music Scraper](https://apify.com/xmolodtsov/tiktok-sound-scraper): videos that use a sound or song

**Social & news:** [Reddit Search Scraper](https://apify.com/xmolodtsov/reddit-scraper): posts by keyword, subreddit or author · [Google News Scraper](https://apify.com/xmolodtsov/google-news-scraper): Google News results as clean full-text articles

**E-commerce:** [Prom.ua Product Search Scraper](https://apify.com/xmolodtsov/prom-ua): Prom.ua product search results, no browser needed · [Amazon Product Search & Bestsellers Scraper](https://apify.com/xmolodtsov/amazon-search-scraper): search results, Best Sellers and product details from Amazon

**Maps & leads:** [Google Maps Scraper](https://apify.com/xmolodtsov/google-maps-scraper): places, leads and emails from Google Maps

**Ads:** [Facebook Ad Library Scraper (Meta Ads Library)](https://apify.com/xmolodtsov/facebook-ads-library-scraper): ads from the Meta Ad Library, with EU reach and payers

### FAQ

#### Is it legal to scrape YouTube transcripts?

This Actor collects only publicly available data. It never logs in and does not access private, members-only or age-restricted videos. Transcripts can still contain personal data, such as names and what people say about themselves, which laws such as the GDPR in the EU and the CCPA in California protect. Collect personal data only when you have a legitimate reason, and store and process it responsibly. Transcripts are also the creator's work: check that your use respects copyright and YouTube's Terms of Service, especially before republishing transcript text. This is not legal advice; if you are unsure, consult a lawyer.

#### Can it translate transcripts into another language?

No. YouTube throttles its translated-caption feature (HTTP 429 "too many requests") too hard for dependable bulk runs, so the Actor doesn't offer translation. Pick a caption language the video already has: `available_languages` lists them all. With **Fall back to the video's own language** on, a video without your languages returns its original-language transcript, which you can translate afterwards with any translation tool.

Some videos are auto-dubbed by YouTube into other languages, and each dub has its own auto-generated captions. If you ask for a dub's language (often English), you get the transcript of the AI dub, not the speaker's own words. The fallback always uses the language actually spoken in the video.

#### What if a video has no captions?

You get a free row with `error_code: "no_captions"`, so you can see the video was checked. The Actor reads YouTube's existing captions and does not run speech-to-text. Recent official music videos often have no captions at all.

#### Does it work with YouTube Shorts?

Yes. Paste Shorts URLs (`youtube.com/shorts/…`) or their video IDs. A channel URL lists the channel's Videos tab, so a channel's Shorts tab is not included; paste the Shorts you want directly.

#### How fast is it?

In our tests, a run of 200 videos took about 95 seconds at the default settings, and 200 long videos (many over three hours) took about 2.5 minutes. Speed depends on video length and YouTube's response times.

#### How do I get every transcript from a channel or playlist?

Paste the channel URL, @handle or playlist URL and set **Max videos per channel or playlist** (up to 5,000). Channels return their latest uploads, newest first; duplicates across the run are removed.

#### Is the text ready for an LLM?

`transcript_text` is one plain string with the segments joined by spaces. Auto-generated captions keep YouTube's sound tags such as `[music]` or `[applause]`, which you may want to strip. Use `transcript` when you need timestamps to cite or chunk by time.

#### Can I download the subtitles as an SRT file?

The Actor returns JSON, CSV or Excel rather than subtitle files, but with **Include timestamped segments** on (the default) every segment in `transcript` has `start` and `end` in seconds, so an SRT file is a few lines away:

```python
def ts(seconds):
    ms = round(seconds * 1000)
    return f"{ms // 3600000:02}:{ms // 60000 % 60:02}:{ms // 1000 % 60:02},{ms % 1000:03}"

srt = "\n".join(f"{i}\n{ts(s['start'])} --> {ts(s['end'])}\n{s['text']}\n" for i, s in enumerate(item["transcript"], 1))
```

#### Does it handle very long videos and livestreams?

Yes. When a multi-day livestream's segments would exceed Apify's 9 MB item limit, the segments are dropped and `transcript_text` is kept; the row's `message` says so.

#### Do I need a proxy, an API key or a YouTube account?

No. A residential proxy is built in and included in the price, and the Actor never logs in. Leave the proxy setting as is unless you want to route through your own proxies.

### Support

Found a bug, a video that should work but doesn't, or a field you need? Open an issue in the **Issues** tab on this Actor's page with the run link, and we'll take a look.

# Changelog

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

# Actor input Schema

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

Required. One per line, mixed freely: video, Shorts or live URLs (watch?v=, youtu.be/, /shorts/, /live/, /embed/), 11-character video IDs, channel URLs or @handles (the channel's latest uploads from its Videos tab), or playlist URLs. Duplicate videos are fetched once. <strong>Free Apify plan:</strong> at most 10 videos per run in total, 5 runs per calendar month (a limit set by the developer, not by Apify).

## `maxVideosPerChannel` (type: `integer`):

How many of the latest videos to take from each channel or playlist URL (channels: newest uploads first; a channel's Shorts tab is not included). Single video URLs are not affected. <strong>Free Apify plan:</strong> a run returns at most 10 videos in total, whatever this value (a limit set by the developer, not by Apify).

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

Caption language codes in order of preference, e.g. en, es, de. The first one the video has wins; "en" also matches regional variants such as en-GB. Captions are not translated: pick languages the video already has (the output's available_languages lists them).

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

Any: human-made subtitles first, auto-generated captions otherwise. Manual only: human-made subtitles only. Auto-generated only: captions from YouTube's speech recognition only. The output's is_auto_generated field shows which one you got.

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

When none of your preferred languages exists, return the transcript in the video's original language. Off: such videos get a free language_not_found row instead.

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

Add the transcript field: segments with start, end and duration in seconds. The plain-text transcript_text is always included; turn this off for smaller results.

## `proxyConfiguration` (type: `object`):

Optional. A residential proxy is built in and included in the price, so leave this as is. Change it only to route requests through your own proxies; YouTube blocks most datacenter IPs.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "maxVideosPerChannel": 10,
  "languages": [
    "en"
  ],
  "captionType": "any",
  "fallbackToAnyLanguage": true,
  "includeTimestamps": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One item per video: transcript segments with timestamps, the full text, caption language and video metadata. Videos without captions get a free row with the reason.

# 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 = {
    "urls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw"
    ],
    "languages": [
        "en"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("xmolodtsov/youtube-transcript-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
    "languages": ["en"],
    "proxyConfiguration": { "useApifyProxy": True },
}

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

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

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

```

## CLI example

```bash
echo '{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "languages": [
    "en"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call xmolodtsov/youtube-transcript-scraper --silent --output-dataset

```

## MCP server setup

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

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/ZirTsSg0tVUdt7Uyv/builds/JOZRYCA3IemnFZdfa/openapi.json
