# YouTube Video Scraper — Views, Likes, Tags & Captions (`andronixmd/youtube-scraper`) Actor

Scrape YouTube video data from video URLs: title, channel, duration, upload date, view, like and comment counts, tags, thumbnail and description, plus caption text in your languages. Export JSON, CSV or Excel. No login and no video download. $0.005 per video or less, captions included.

- **URL**: https://apify.com/andronixmd/youtube-scraper.md
- **Developed by:** [Sergey Andronik](https://apify.com/andronixmd) (community)
- **Categories:** Videos, Social media, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 video scrapeds

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

Scrape **YouTube video data** from video URLs into clean JSON. For each public YouTube
video, YouTube Video Scraper returns the title, channel, duration, upload date, view,
like and comment counts, tags, categories, thumbnail and description, plus the caption
text and timed caption lines in the languages you choose. The video itself is never
downloaded and no AI is run, so a video costs $0.005 or less, captions included. Use
the output for spreadsheets, dashboards, search indexes, research or LLM prompts. No
YouTube login, cookies or API key are needed.

### What YouTube data you get

| Field | Content |
|---|---|
| `url`, `video_id` | The URL you submitted and YouTube's video ID. |
| `title`, `description` | The video title and description. |
| `author_username`, `author_name`, `author_id`, `author_url` | The channel. |
| `author_follower_count`, `author_is_verified` | The channel's subscriber count (as YouTube rounds it) and verified badge. |
| `published_at`, `upload_date` | Publish time and date. |
| `released_at` | Start time of a live stream or premiere. Only for those. |
| `duration_s`, `is_live` | Length in seconds, and whether the video is a live stream. |
| `live_status`, `was_live`, `media_type` | `not_live`, `is_live`, `is_upcoming`, `was_live` or `post_live`; whether it was ever live; `video`, `short` or `livestream`. |
| `availability`, `age_limit`, `playable_in_embed`, `license` | Public or unlisted, minimum viewer age, whether it can be embedded, and its license when it has one (for example Creative Commons). |
| `webpage_url` | The canonical watch URL, whichever form you submitted. |
| `width`, `height`, `fps`, `resolution` | Of the best video stream. |
| `hashtags` | The `#hashtags` written in the description. |
| `video_chapters` | The creator's chapters, `[{ "title", "start_s", "end_s" }]`. Only when the video has them. |
| `available_caption_languages` | The caption tracks YouTube lists in the video's own languages, `[{ "language", "kind", "name" }]`, whether or not you asked for captions. YouTube's machine translations are not listed; the list stops at 100 entries. |
| `view_count`, `like_count`, `comment_count` | Counts as YouTube shows them. |
| `tags`, `categories`, `language` | Tags, YouTube categories and the video's language. |
| `thumbnail_url` | Thumbnail link. |
| `captions_available`, `caption_language`, `caption_kind` | Whether a caption track in your languages was found, which one, and whether it is `manual` (creator) or `auto` (YouTube-generated). |
| `caption_text`, `caption_segments` | The caption text, and the caption lines with start and end seconds. |
| `billing` | Whether this row was charged. |

The Dataset has two views: **Videos** (title, channel, date, duration, counts and caption
status) and **Captions text**.

### How to scrape YouTube videos

1. **Paste your video URLs.** Watch links, `youtu.be` links, Shorts, live replays and
   embed links work, up to 1,000 per run. For a whole channel or playlist, use
   `channelUrls` instead.
2. **Choose captions.** Keep **Include captions** on and list your languages in order,
   for example `["en", "es"]`. Turn it off if you only need the video data.
3. **Set a run budget** in the run options if you want a hard cap on the spend.
4. **Start the run and open the Dataset.** One row per URL. Export it as JSON, CSV or
   Excel, or read it through the API.

### Input

```json
{
  "urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"],
  "includeCaptions": true,
  "captionLanguages": ["en"]
}
```

| Field | What it does |
|---|---|
| `urls` | Up to 1,000 public YouTube video URLs. Required unless you set `datasetId`. |
| `datasetId` | Read URLs from another run's dataset. See [Chain from another Actor](#chain-from-another-actor). |
| `datasetUrlField`, `maxItems` | The dataset field that holds the URL (detected when empty), and how many rows to read (default 100, at most 1,000). |
| `includeCaptions` | Add the caption text and timed lines. Default `true`. |
| `captionLanguages` | Up to 10 language codes in priority order. Default `["en"]`. The first language with a caption track is returned; creator captions are preferred over auto-generated ones. |

### Start from a channel or playlist

Put channel or playlist URLs in **YouTube channels and playlists** (`channelUrls`) to scrape their newest videos:

```json
{
  "channelUrls": ["https://www.youtube.com/@TED/videos"],
  "maxItemsPerSource": 50,
  "includeCaptions": false
}
```

- Channel URLs: `/@handle`, `/channel/UC…`, `/c/…` and `/user/…`, optionally with a `/videos`, `/shorts` or `/streams` tab. Without one, **Channel tab** (`channelTab`, default `videos`) decides. Playlist URLs: `/playlist?list=…`, in playlist order.
- Each row adds `source_url`, `source_type` (`youtube_channel` or `youtube_playlist`) and `source_title`.
- `publishedAfter` (YYYY-MM-DD) is approximate on YouTube: channel and playlist pages show only a relative age such as "3 weeks ago", so videos up to one such unit older can be included. Shorts have no date at all, so `publishedAfter` cannot be combined with the Shorts tab.
- `publishedAfter` filters only the entries that were listed. A channel tab lists newest first, `maxItemsPerSource` deep, so it finds every recent video. A playlist keeps its creator's order, which can be oldest first; with `publishedAfter` it is listed up to 200 entries deep, and the first `maxItemsPerSource` entries on or after the date are kept. Newer videos further down a longer playlist are not found.
- The listing request goes from Apify's servers directly to YouTube, not through the residential proxy that video data uses.
- Every listed video gets the full per-video data, as for a URL you submit.
- `maxItemsPerSource` (default 20, at most 200) limits how many of the newest items each source contributes. A run holds at most 1,000 items in total, counting `urls` and dataset rows.
- Each listed item is processed and charged exactly like a URL you submit. Listing itself is free.
- The example link in the URL list is processed and charged too: remove it when you want only listed items.
- Listed items run after `urls` and dataset rows, source by source in input order; an item already in the run is skipped.
- A source that cannot be listed becomes one error row with `source_url` and an `error` naming it (for example `channelUrls[1]: …`), and it is not charged. The other sources continue. If no source lists anything, the run fails before any charge.
- The listing is saved when the run starts, so a restarted run keeps the same items even if the source changed meanwhile.

### Scheduled runs: only new items

Turn on **Skip items processed by earlier runs** (`skipPreviouslyProcessed`) and run the
Actor on a [schedule](https://docs.apify.com/platform/schedules) to get only what is new
since the last run.

- The Actor keeps a key-value store named `polyglot-processed-youtube-scraper` in your Apify
  account, or `polyglot-processed-youtube-scraper--<memoryKey>` when you set **Memory name**
  (`memoryKey`), so separate schedules can keep separate memories. Other Actors keep
  their own stores, even with the same `memoryKey`. It records each item after the item
  is delivered and charged; failed items are tried again next time.
- It applies to every URL in the run: listed ones, dataset rows and the items listed
  from sources.
- When nothing is new, the run ends without any charge. Only error rows appear then, for
  sources or dataset rows that could not be used.
- If the store cannot be opened or read, the run stops before any charge, and the next
  scheduled run tries again. If an item's record cannot be written, the item is still
  delivered and the log says so; a later run may process and charge it again.
- Delete the store in Apify Console (Storage → Key-value stores) to start over. Do not run
  two runs with the same memory at the same time: both would process the same new items.

### Chain from another Actor

Run another run that lists YouTube videos first, then paste the dataset ID of that run into **Dataset ID**
(`datasetId`) to get data and captions for every one of its YouTube videos:

```json
{ "datasetId": "<dataset ID from the other run>", "maxItems": 200 }
```

- The Actor reads up to `maxItems` rows (default 100) with your own run's Apify token and
  takes each row's URL from `webVideoUrl`, `url`, `postUrl`, `videoUrl`, `inputUrl` or `link`, or from the field you name in `datasetUrlField`.
- Dataset URLs run after any `urls` you list, in dataset order; a dataset URL that is
  already in the run is skipped. A run takes at most 1,000 URLs in total.
- A row without a usable URL becomes an error item with its `dataset_row` index, and it is
  not charged. Items from the dataset also carry `dataset_row`, so you can join the results
  back to the source rows.
- The example link in the URL list is processed and charged too: remove it when you want only the dataset's items.

### Output example

An illustrative result for "Me at the zoo": the counts and channel come from an Apify run
on 2026-10-30, and the captions from the video's real English caption track. It is not
one recorded run; the description and the later caption lines are left out.

```json
{
  "url": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "video_id": "jNQXAC9IVRw",
  "title": "Me at the zoo",
  "author_username": "jawed",
  "author_name": "jawed",
  "author_url": "https://www.youtube.com/@jawed",
  "upload_date": "2005-04-24",
  "duration_s": 19,
  "view_count": 447196495,
  "like_count": 20119541,
  "thumbnail_url": "https://i.ytimg.com/vi_webp/jNQXAC9IVRw/maxresdefault.webp",
  "captions_available": true,
  "caption_language": "en",
  "caption_kind": "manual",
  "caption_text": "All right, so here we are, in front of the elephants the cool thing about these guys is that they have really... really really long trunks and that's cool (baaaaaaaaaaahhh!!) and that's pretty much all there is to say",
  "caption_segments": [
    { "t0": 1.2, "t1": 3.36, "text": "All right, so here we are, in front of the elephants" },
    { "t0": 5.318, "t1": 7.974, "text": "the cool thing about these guys is that they have really..." }
  ],
  "billing": {
    "status": "paid",
    "events": [{ "event": "video-scraped", "requested_count": 1, "charged_count": 1, "state": "confirmed" }]
  }
}
```

- A field is left out when YouTube does not show it for that video.
- A video without a caption track in your languages still returns its data, with
  `captions_available: false`, and is charged as usual.
- A video whose listed caption track cannot be fetched returns a row with `url` and
  `error`, and is not charged.
- The `author_*` fields have the same names in our TikTok and Instagram scrapers.

### How YouTube data is fetched

- On Apify, the video's metadata and its caption track are requested through Apify's
  residential proxy, with a traffic limit for each video. That cost is included in the
  per-video price.
- The Actor uses no YouTube login and no cookies.
- When YouTube blocks a request, returns incomplete video data, or the video is
  unavailable, the row returns an error and is not charged.
- **Captions up to 5 hours.** With **Include captions** on, a video longer than 5 hours
  returns an error and is not charged. Turn captions off to get its video data.

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

Pay per event, with no subscription. The
[Pricing tab](https://apify.com/andronixmd/youtube-scraper/pricing) shows the current
prices.

| Event | Price | When |
|---|---:|---|
| Run setup | $0.002 | Once per run. |
| Video scraped | $0.004–$0.005, by discount tier | Per video whose data was returned, with or without captions. |

| Apify Store discount tier | Video scraped |
|---|---:|
| Free | $0.005 |
| Bronze | $0.005 |
| Silver | $0.0045 |
| Gold and above | $0.004 |

| Videos in one run | Cost |
|---|---:|
| 1 | $0.002 + $0.005 = **$0.007** |
| 100 | $0.002 + 100 × $0.005 = **$0.502** |
| 1,000 | $0.002 + 1,000 × $0.005 = **$5.002** |

Costs above use the Free and Bronze price. At Gold, 1,000 videos cost $4.002.

Video length does not change the price, and captions cost nothing extra. URLs that fail
or are rejected are not charged the per-video fee. Apify's own small run-start event is
separate and is shown on the Pricing tab.

### Use it from the API

Start a run with the [Apify API client for JavaScript](https://docs.apify.com/api/client/js/)
and read the results:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN })
const run = await client.actor('andronixmd/youtube-scraper').call(
  { urls: ['https://www.youtube.com/watch?v=jNQXAC9IVRw'], captionLanguages: ['en'] },
  { maxTotalChargeUsd: 0.1 },
)
const { items } = await client.dataset(run.defaultDatasetId).listItems()
for (const item of items) console.log(item.url, item.error ?? item.view_count)
```

Or start one run over HTTP:

```sh
curl -X POST \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"urls":["https://www.youtube.com/watch?v=jNQXAC9IVRw"]}' \
  'https://api.apify.com/v2/actors/andronixmd~youtube-scraper/runs?maxTotalChargeUsd=0.1'
```

The Actor also works from n8n and other automation tools through Apify's integrations. Schedule a run with the same URLs to track how the counts change over time.

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

Connect your agent to `https://mcp.apify.com`. It can find this Actor with `search-actors`, or
call it by name, `andronixmd/youtube-scraper`, with `call-actor`. Use `fetch-actor-details` first for the
input schema and pricing. Every call starts a new paid run, so always set
`callOptions.maxTotalChargeUsd` and keep the `urls` list short:

```json
{ "actor": "andronixmd/youtube-scraper", "input": { "urls": ["https://www.youtube.com/watch?v=jNQXAC9IVRw"], "includeCaptions": true, "captionLanguages": ["en"] }, "callOptions": { "maxTotalChargeUsd": 0.1 } }
```

`call-actor` returns the run status and storage IDs, not the results. Read the rows with
`get-dataset-items` and the run's dataset ID. The fields to use are `title`, `view_count`, `like_count` and `error`.

With an Apify account, use your API token or MCP OAuth. Without one, an agent can buy a prepaid token from [Apify AGI](https://agi.apify.com) over
x402 or MPP and use it as the Bearer token. It starts at $1, the balance is a hard spending cap,
and the token expires after 14 days. This is experimental; see
[agentic payments](https://docs.apify.com/agents/payments).

### Limitations

- **Public videos only.** Private, members-only, age-restricted and region-blocked
  videos, and anything that needs a login, are not supported.
- **Videos, channels and playlists.** Search URLs are not accepted. One URL in a wrong
  format rejects the whole input before the run starts. Channel listing from Apify's
  servers has not been tested at scale; a channel that cannot be listed becomes an error
  row that is not charged.
- **Not every video works.** YouTube can block requests or serve incomplete data, so
  some URLs return an error row. Check each row's `error` field and retry failed URLs in
  a later run.
- **Restarted runs.** If a run is restarted or resurrected, the remaining videos fail
  without a charge; start a new run for them. A run that Apify moves to another server
  continues normally. After 20 videos in a row fail without a
  charge, the run stops requesting YouTube data and the rest fail the same way.
- **Data and captions only.** No comment text, channel statistics or video and audio
  files, and no transcript for videos without captions: for those, use
  [YouTube Transcript](https://apify.com/andronixmd/youtube-transcript).

### FAQ

**Where do the captions come from, and are they accurate?**
They are YouTube's own caption tracks, as published. Creator captions are usually
accurate; auto-generated ones can contain errors, and `caption_kind` tells you which you
got. For videos without captions, or for speaker labels, use
[YouTube Transcript](https://apify.com/andronixmd/youtube-transcript), which adds AI
speech-to-text.

**Which caption languages can I get?**
Any language the video has a caption track in. List up to 10 codes in priority order;
the first available one is returned.

**Are the numbers live?**
They are what YouTube shows at the moment of the run. Run it again later for new
numbers.

**Does it scrape channels, playlists, comments or search results?**
Channels and playlists, yes: put them in `channelUrls`, and each listed video is scraped
in full. Comments and search results, no.

**Where is my data kept?**
In your Apify run storage, under your account's access and retention settings. Use the
data in line with YouTube's terms and the law that applies to you.

### Related Actors

- [YouTube Transcript](https://apify.com/andronixmd/youtube-transcript): transcripts
  from captions, with AI speech-to-text for videos without them, plus SRT, VTT and
  speaker labels.
- [TikTok Video Scraper](https://apify.com/andronixmd/tiktok-scraper) and
  [Instagram Reel Scraper](https://apify.com/andronixmd/instagram-reel-scraper): the same
  kind of data for TikTok and Instagram Reels.
- [Audio & Video Transcriber](https://apify.com/andronixmd/speech-to-text-transcriber):
  transcripts of podcasts, audio and video files and other sites.

# Actor input Schema

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

Public YouTube video links, one result per URL. Accepted forms: youtube.com/watch?v=…, youtu.be/…, youtube.com/shorts/…, /live/… and /embed/… (also m.youtube.com and music.youtube.com watch links). Channel and playlist URLs go in YouTube channels and playlists below; search and private video URLs are rejected. Submit up to 1,000 URLs, or leave this list empty and fill in channels and playlists or Dataset ID below.

## `includeCaptions` (type: `boolean`):

Warning: caption_text can exceed 50,000 characters per video. To avoid overflow, read rows with limit 1, or turn this off when only metadata is needed. Adds caption_text (plain text) and caption_segments (timed lines) when the video has a caption track in one of your chosen languages. Read the whole caption_segments field (fields=caption_segments): dotted paths such as caption_segments.t0 return nothing. No audio is downloaded and no AI speech-to-text is run.

## `captionLanguages` (type: `array`):

Language codes in priority order, for example en, ru, pt-BR. The first language with an available caption track is returned; manual captions are preferred over auto-generated ones. Ignored when captions are off.

## `channelUrls` (type: `array`):

Channel URLs such as https://www.youtube.com/@TED, /channel/UC…, /c/… or /user/…, optionally ending in /videos, /shorts or /streams, and playlist URLs such as https://www.youtube.com/playlist?list=PL…. Each listed video is processed exactly like a video URL above, and each row names the channel or playlist it came from. Up to 100 sources. The example URL above is processed and charged too: remove it if you want only listed videos.

## `channelTab` (type: `string`):

Which tab of a channel to list when its URL names none. Playlists ignore it.

## `maxItemsPerSource` (type: `integer`):

List at most this many of the newest videos from each source (up to 200). Each listed item is processed and billed like a URL you submit; listing itself is free. A run holds at most 1,000 items in total, counting URLs and dataset rows.

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

Optional date as YYYY-MM-DD (UTC); videos published before it are left out. Approximate: YouTube shows only relative ages ("3 weeks ago") on channel and playlist pages, so videos up to one such unit older can pass. Shorts carry no date and cannot be filtered. Only listed entries are filtered: a channel tab lists newest first, maxItemsPerSource deep; a playlist keeps its creator's order and is listed up to 200 entries deep, so newer videos further down are not found.

## `datasetId` (type: `string`):

The example URL above is processed and charged too: remove it if you want only the dataset's items. Pick the dataset, or paste its ID, from another run that lists YouTube videos to get data and captions for every video in it. The Actor reads the rows with your run's own token and processes each row's URL like a URL above, after the listed URLs. Rows without a usable URL become uncharged error items. A named dataset works as username~dataset-name.

## `datasetUrlField` (type: `string`):

Optional. The row field that holds the URL, for example webVideoUrl, or a dotted path such as video.url. Leave it empty to use the first accepted URL from webVideoUrl, url, postUrl, videoUrl, inputUrl or link.

## `maxItems` (type: `integer`):

Read at most this many dataset rows, in dataset order. A run processes at most 1,000 URLs in total, counting the list above.

## `skipPreviouslyProcessed` (type: `boolean`):

Remember every item that was delivered and charged, in a key-value store in your Apify account, and skip those items in later runs. Use it with a schedule to get only new videos or episodes. Items that failed are tried again next time. When every item was already processed, the run ends without any charge. If the store cannot be read, the run stops before any charge and the next run tries again.

## `memoryKey` (type: `string`):

Optional. Keeps a separate memory for this schedule, for example my-weekly-podcasts. The store is named polyglot-processed-youtube-scraper--<name>, and other Actors never share it; without a name every run of this Actor shares polyglot-processed-youtube-scraper. Delete the store in Apify Console (Storage, Key-value stores) to start over. Used only with the option above.

## Actor input object example

```json
{
  "urls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw"
  ],
  "includeCaptions": true,
  "captionLanguages": [
    "en"
  ],
  "channelTab": "videos",
  "maxItemsPerSource": 20,
  "maxItems": 100,
  "skipPreviouslyProcessed": false
}
```

# Actor output Schema

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

One Dataset item per submitted URL, with video metadata, optional caption text, confirmed billing and errors. Results are in the default dataset; item-NNN key-value records are internal resume state.

# 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"
    ],
    "captionLanguages": [
        "en"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("andronixmd/youtube-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"],
    "captionLanguages": ["en"],
}

# Run the Actor and wait for it to finish
run = client.actor("andronixmd/youtube-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"
  ],
  "captionLanguages": [
    "en"
  ]
}' |
apify call andronixmd/youtube-scraper --silent --output-dataset

```

## MCP server setup

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