# YouTube Scraper (`ecommerce_leads/youtube-scraper`) Actor

Search videos, get playlists, single videos with comments and all in any language

- **URL**: https://apify.com/ecommerce\_leads/youtube-scraper.md
- **Developed by:** [Monster Leads](https://apify.com/ecommerce_leads) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.94 / 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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## YouTube Scraper — any language

Search videos, get playlists, single videos with comments — and all in any language.

Scrape YouTube **channels**, **playlists**, **search results** and **individual
videos** without the official YouTube Data API and without a headless browser. Every
request goes through a rotating proxy, handled for you — there is nothing to configure.

### Any language

Set `language` and YouTube returns that locale's version of the data — titles,
descriptions, view counts, dates and subscriber counts:

```json
{ "mode": "channel", "channel": "@MrBeast", "language": "es" }
```

| `language` | Title returned |
| --- | --- |
| `en` | I Survived The Most Extreme Places On Earth |
| `es` | Sobrevivimos a los Lugares más Extremos del Mundo |
| `de` | Wir haben an den extremsten Orten überlebt |
| `pt-BR` | Nós Sobrevivemos aos Lugares Mais Extremos do Mundo |

**Want the same video in several languages?** Run the actor once per language and
join the results on `videoId`, which is stable across every locale. That gives you one
row per language for the same video — useful for building localised catalogues,
comparing how a channel positions itself per market, or filling a multi-language
database from a single source.

Because the language is sent explicitly, results stay consistent no matter which
country the rotating proxy exits from. Without it YouTube guesses from the exit IP and
a single run can come back with mixed languages.

### What it does

| Mode | Input | Returns |
| --- | --- | --- |
| `channel` | channel id, `@username` or channel URL | videos, shorts or streams from that channel |
| `playlist` | playlist id (`PL...`, `UU...`) | every video in the playlist |
| `search` | a search term | videos, channels, playlists or movies |
| `video` | a list of video ids or URLs | full metadata per video, optionally with top comments |

### Input

Pick a **mode**, then fill in only that mode's fields. Everything else is ignored.

| Mode | Required field | Optional fields for that mode |
| --- | --- | --- |
| `channel` | `channel` | `contentType`, `sortBy` |
| `playlist` | `playlistId` | — |
| `search` | `searchQuery` | `resultsType`, `searchSortBy` |
| `video` | `videoIds` | `commentsPerVideo` |

`maxItems` and `language` apply to every mode.

Minimal channel run:

```json
{
  "mode": "channel",
  "channel": "@LinusTechTips",
  "maxItems": 100
}
```

Playlist run:

```json
{
  "mode": "playlist",
  "playlistId": "PLZbbT5o_s2xrwRnXk_yCPtnqqo4_u2YGL"
}
```

Search run:

```json
{
  "mode": "search",
  "searchQuery": "python tutorial",
  "resultsType": "video",
  "searchSortBy": "view_count",
  "maxItems": 50
}
```

Single video run, with the top 20 comments:

```json
{
  "mode": "video",
  "videoIds": [
    "dQw4w9WgXcQ",
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/9bZkp7q19f0"
  ],
  "commentsPerVideo": 20
}
```

#### Field reference

**Required, depending on mode**

- **`channel`** *(Channel mode)* — channel id (`UC...`), `@username`, or full channel
  URL.
- **`playlistId`** *(Playlist mode)* — the id after `list=` in a playlist URL.
- **`searchQuery`** *(Search mode)* — what to search for.
- **`videoIds`** *(Single videos mode)* — one entry per video. Bare ids, watch URLs,
  `youtu.be` links and shorts URLs are all accepted.

**Optional, mode-specific**

- **`contentType`** *(Channel)* — `videos`, `shorts` or `streams`. Defaults to
  `videos`.
- **`sortBy`** *(Channel)* — `newest`, `popular` or `oldest`. Defaults to `newest`.
- **`resultsType`** *(Search)* — `video`, `channel`, `playlist` or `movie`. Defaults
  to `video`.
- **`searchSortBy`** *(Search)* — `relevance`, `upload_date`, `view_count` or
  `rating`. Defaults to `relevance`.
- **`commentsPerVideo`** *(Single videos)* — top comments per video. `0` skips them.
  Included in the per-video price, so 20 costs the same as 0.

**Optional, every mode**

- **`maxItems`** — stop after this many results. Leave empty to scrape everything.
- **`language`** — `Accept-Language` sent to YouTube, eg `en`, `de`, `es`, `pt-BR`.
  Defaults to `en`. See [Language](#language) below.

Proxy rotation, retries and request pacing are handled automatically — there is
nothing to configure.

### Output

Each dataset item for `channel` mode:

```json
{
  "videoId": "sL6OWsT47zc",
  "title": "Apple just Copied Microsoft's Worst Idea",
  "is_live": false,
  "url": "https://www.youtube.com/watch?v=sL6OWsT47zc",
  "thumbnail": "https://i.ytimg.com/vi/sL6OWsT47zc/hqdefault.jpg",
  "source": "channel"
}
```

`playlist` mode adds which playlist the video came from, on every item, so a dataset
holding several playlists stays groupable:

```json
{
  "videoId": "0UG_SZpo750",
  "title": "T1 vs GEN | 2026 LCK Road to MSI: Final Round",
  "is_live": false,
  "url": "https://www.youtube.com/watch?v=0UG_SZpo750",
  "thumbnail": "https://i.ytimg.com/vi/0UG_SZpo750/hqdefault.jpg",
  "playlistId": "PLTL19WNP0Lgc",
  "playlistTitle": "2026 Road to MSI FULL VOD",
  "playlistUrl": "https://www.youtube.com/playlist?list=PLTL19WNP0Lgc",
  "source": "playlist"
}
```

Search mode adds `duration`, `views`, `published` and `channel`.

Each dataset item for `video` mode carries the full metadata:

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "description": "The official video for ...",
  "viewCount": 1813981533,
  "likes": 19381188,
  "lengthSeconds": 213,
  "publishDate": "2009-10-24T23:57:33-07:00",
  "channel": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "subscribers": "4.54M subscribers",
  "category": "Music",
  "keywords": ["rick astley", "nggyu"],
  "isLive": false,
  "commentsFetched": 20,
  "comments": [
    {
      "commentId": "Ugzge340dBgB75hWBm54AaABAg",
      "text": "can confirm: he never gave us up",
      "author": "@YouTube",
      "authorChannelId": "UCBR8-60-B28hp2BmDPdntcQ",
      "authorIsVerified": true,
      "likes": 313000,
      "likesText": "313K",
      "replyCount": 963,
      "publishedText": "1 year ago"
    }
  ]
}
```

Comment like counts are abbreviated by YouTube, so `likes` is an expansion of
`likesText` (`"313K"` becomes `313000`) and is approximate above 1,000. The video's
own `likes` and `viewCount` are exact.

### Pricing

This actor uses **pay per event**:

| Event | Applies to | Price |
| --- | --- | --- |
| Listing item scraped | `channel`, `playlist`, `search` | **$0.001** per item ($1 per 1,000) |
| Video details scraped | `video` | **$0.01** per video |

A `video` item costs more because it needs its own page request plus a comments
continuation, and returns full metadata (description, likes, duration, keywords,
category) together with the top comments. Comments are **included** in that $0.01 —
they are not billed separately, so 20 comments cost the same as 0.

You are charged only for items actually stored in the dataset, so a run that returns
nothing costs nothing.

### Notes and limits

#### What `language` does and does not translate

| What | Effect of `language` |
| --- | --- |
| View counts, dates, subscriber counts | Always localised — `1.8B views` vs `1,8 Mrd. Aufrufe` |
| Video titles and descriptions | Localised **only where the creator supplied a translation** |

A channel that publishes no translations returns its original titles in every
language, so `language` is not a machine translator — it returns what the creator
actually published for that locale.

Numeric `viewCount` and `likes` are parsed to plain integers, so they are safe to
compare and deduplicate across languages. `shortViewCount`, `subscribers` and
`likesText` keep YouTube's localised formatting.

If you need a title in its **original** language regardless of translations, fetch it
from the YouTube Data API using the returned `videoId`.

#### Other limits

- **`popular` / `oldest` sorting** is only available on channel tabs that have enough
  videos for YouTube to render the filter chips. Small channels fall back to newest.
- **No download links.** Results contain the canonical `watch?v=` URL. Direct
  `googlevideo.com` stream URLs are deliberately not extracted: they expire within
  hours and are bound to the IP that fetched them, so they would be broken for most
  users by the time they were read.

# Actor input Schema

## `mode` (type: `string`):

Choose one. Then fill in ONLY the fields for that mode below; every other field is ignored.

• Channel — all videos, shorts or streams from a channel
• Playlist — every video in a playlist
• Search — search results
• Single videos — full details for specific videos, with comments

## `channel` (type: `string`):

REQUIRED IF MODE = CHANNEL. Ignored in every other mode.

Accepts a channel id (UC...), an @username, or a full channel URL.
Examples: @LinusTechTips · UCXuqSBlHAE6Xw-yeJA0Tunw · https://www.youtube.com/@MrBeast

## `contentType` (type: `string`):

Optional. Used only in Channel mode. Which tab of the channel to scrape. Defaults to Videos.

## `sortBy` (type: `string`):

Optional. Used only in Channel mode. Popular and Oldest need a channel with enough videos for YouTube to show its filter chips; smaller channels fall back to Newest.

## `playlistId` (type: `string`):

REQUIRED IF MODE = PLAYLIST. Ignored in every other mode.

The id after 'list=' in a playlist URL.
Example: PLZbbT5o\_s2xrwRnXk\_yCPtnqqo4\_u2YGL

## `searchQuery` (type: `string`):

REQUIRED IF MODE = SEARCH. Ignored in every other mode.

What to search YouTube for.
Example: python tutorial

## `resultsType` (type: `string`):

Optional. Used only in Search mode. What kind of results to return. Defaults to Videos.

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

Optional. Used only in Search mode. Defaults to Relevance.

## `videoIds` (type: `array`):

REQUIRED IF MODE = SINGLE VIDEOS. Ignored in every other mode.

One entry per video. Accepts bare ids, watch URLs, youtu.be links or shorts URLs.
Examples: dQw4w9WgXcQ · https://www.youtube.com/watch?v=dQw4w9WgXcQ · https://youtu.be/dQw4w9WgXcQ

## `commentsPerVideo` (type: `integer`):

Optional. Used only in Single videos mode. How many top comments to include with each video. Set 0 to skip comments. Comments are included in the per-video price, so 20 costs the same as 0.

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

Optional. Applies to every mode. Stop after this many results. Leave empty to scrape everything the target has.

## `language` (type: `string`):

Optional. Applies to every mode. Defaults to 'en'.

YouTube returns this locale's version of the data: view counts, dates and subscriber counts are always localised, and titles and descriptions are localised where the creator published a translation.

Examples: en · es · de · fr · pt-BR · ja

## Actor input object example

```json
{
  "mode": "channel",
  "channel": "@LinusTechTips",
  "contentType": "videos",
  "sortBy": "newest",
  "resultsType": "video",
  "searchSortBy": "relevance",
  "commentsPerVideo": 20,
  "maxItems": 50,
  "language": "en"
}
```

# Actor output Schema

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

One item per result. Channel and playlist items carry videoId, title, url and thumbnail; playlist items also carry the playlist they came from; search items add duration, views and channel; single-video items carry full metadata and comments.

## `runInConsole` (type: `string`):

Open the run in Apify Console.

# 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 = {
    "channel": "@LinusTechTips"
};

// Run the Actor and wait for it to finish
const run = await client.actor("ecommerce_leads/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 = { "channel": "@LinusTechTips" }

# Run the Actor and wait for it to finish
run = client.actor("ecommerce_leads/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 '{
  "channel": "@LinusTechTips"
}' |
apify call ecommerce_leads/youtube-scraper --silent --output-dataset

```

## MCP server setup

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