# YouTube Scraper — Search, Channels, Videos & Playlists (`datamule/youtube-data-scraper`) Actor

All-in-one YouTube data Actor: keyword search, per-video detail (exact player fields when served, else exact-id list metadata), channel videos/shorts/live and whole playlists — one flat schema, no API key, no login, no proxy. Pay per result: 1 unit per row; failed or empty lookups never billed.

- **URL**: https://apify.com/datamule/youtube-data-scraper.md
- **Developed by:** [Datamule](https://apify.com/datamule) (community)
- **Categories:** Developer tools, Social media
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.40 / 1,000 youtube results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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 — Search, Channels, Videos & Playlists

An all-in-one Actor for public YouTube data. No API key, no Google quota, no
login, no cookies, no proxy required. Four modes over one flat output schema, so
a search row, a channel row and a playlist item all land in the same dataset with
a `type` field telling you what each one is.

### Modes

| Mode | Input field | What you get |
|---|---|---|
| `search` | `searchQueries` | Keyword search results, paginated — one row per video |
| `videos` | `videoUrls` | One row per resolvable exact video ID (an unavailable ID yields no row and is never charged): exact player detail (exact view/like counts, publish date, category, keywords) when YouTube serves the player, otherwise exact-ID list metadata |
| `channel` | `channelUrls` | A channel's **Videos**, **Shorts** and **Live** tabs, plus a channel summary row |
| `playlist` | `playlistUrls` | Every item of a playlist, in order, with its position |

Handles (`@name`), `/channel/UC…`, `/c/…`, `/user/…`, `youtu.be/…`, `/shorts/…`,
`?v=…` and bare 11-character IDs are all accepted and normalised for you.

### Pricing — pay per result, nothing else

There is **no actor-start fee**. You are charged only for rows actually
delivered: every `video`, `short`, `channel` or `playlistItem` row costs
**1 result unit**.

A video that is unavailable, a channel that will not resolve, or a playlist that
returns nothing emits **no row and costs you nothing**. Use `maxItems` to put a
hard ceiling on any run.

### Example input

```json
{
  "mode": "search",
  "searchQueries": ["python tutorial"],
  "maxItems": 25,
  "language": "en",
  "region": "US"
}
```

Every video of a channel's Shorts and Videos tabs:

```json
{
  "mode": "channel",
  "channelUrls": ["@YouTube"],
  "channelTabs": ["videos", "shorts"],
  "maxItems": 100
}
```

### Output

Every row carries `type`, `id`, `url`, `title` and provenance (`_mode`,
`_source`, `_input`, `_language`, `_region`). Depending on the type you also get
`channelId` / `channelName` / `channelUrl`, `durationSeconds` / `durationText`,
`viewCount` / `viewCountText`, `likeCount`, `publishedAt` / `publishedText`,
`category`, `keywords`, `thumbnailUrl` and `playlistId` / `playlistPosition`.

Counts that YouTube publishes only as human text ("1.2M views") are parsed to an
integer **and** kept verbatim, so you never have to guess how a number was rounded.

### Notes and honest limits

- Only **public** data is read. Private, members-only, age-gated-login and
  region-blocked videos are skipped, not faked.
- Search and channel-tab list rows carry YouTube's own approximate view counts and
  relative publish text ("8 months ago").
- `mode: "videos"` returns one row for each **resolvable** ID you list, and every
  delivered row belongs to the exact ID you asked for. It returns
  YouTube's exact player fields (`viewCount`, `likeCount`, `publishedAt`,
  `uploadedAt`, `category`, `keywords`) when YouTube serves the player payload to
  the run's IP. When the player is refused — which is common from plain
  datacenter IPs — the row falls back to the same exact-ID list metadata as
  search (`viewCountText`, `publishedText`) and the exact-only fields are simply
  omitted rather than guessed. Either way the row is real data for the ID you
  asked for, never a substitute video.
- If a run resolves zero rows it exits non-zero instead of reporting an empty
  success, so a broken input never looks like a clean run.

This is an independent implementation built against public YouTube client
behaviour. It is not affiliated with, endorsed by, or derived from YouTube,
Google, or any other Apify Actor.

# Actor input Schema

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

What to scrape. 'search' = keyword search results. 'videos' = per-video detail for the exact video URLs/IDs you list: exact player fields (exact view/like counts, exact publish date, category, keywords) when YouTube serves the player to the run, otherwise exact-id list metadata (approximate view text and relative publish text, exact-only fields omitted). 'channel' = a channel's Videos / Shorts / Live tabs. 'playlist' = every item of a playlist, in order. Each mode reads its own input field below.

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

One or more keyword queries. Each query is paginated up to 'Max items per target'. Only used when mode = search.

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

Watch URLs, Shorts URLs, youtu.be links, or bare 11-character video IDs. Only used when mode = videos. Each resolvable listed video returns one row, and every delivered row belongs to the exact id you requested: exact player metadata when YouTube serves the player to the run, otherwise the exact-id list metadata (approximate view text, relative publish text) with the exact-only fields omitted.

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

Channel handles (@name), /channel/UC… URLs, /c/… or /user/… URLs, or bare UC… channel IDs. Handles are resolved to a channel ID automatically. Only used when mode = channel.

## `playlistUrls` (type: `array`):

Playlist URLs (…?list=PL…) or bare playlist IDs. Items are returned in playlist order with their position. Only used when mode = playlist.

## `channelTabs` (type: `array`):

Which channel tabs to read when mode = channel. Shorts are emitted with type 'short'; videos and live streams with type 'video'.

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

Hard cap on the total number of rows this run will emit and bill across every target. The run stops as soon as it is reached. Billing is per delivered row: every video/short/channel/playlist row costs 1 result unit. Nothing that fails or comes back empty is billed.

## `maxItemsPerTarget` (type: `integer`):

Cap per individual query / channel tab / playlist, so one huge target cannot consume the whole run budget. Defaults to the whole-run cap.

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

Interface language (hl), e.g. 'en', 'de', 'ja'. Affects titles and human-readable metadata text.

## `region` (type: `string`):

Two-letter region code (gl) used for results, e.g. 'US', 'GB', 'SG'. Affects ranking and availability.

## `includeChannelInfo` (type: `boolean`):

When on, mode = channel also emits one 'channel' row per channel (title, description, handle URL, keywords) in addition to its videos.

## `timeoutSecs` (type: `integer`):

Per-request timeout for each YouTube call.

## `maxRetries` (type: `integer`):

Bounded retry attempts per request for transient failures (HTTP 429/5xx and network errors), with exponential backoff.

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

Optional proxy for YouTube requests. All four modes work without a proxy; enable one only if you want IP rotation on very large runs. Proxy traffic is billed by the Apify platform on top of this Actor's per-result price.

## Actor input object example

```json
{
  "mode": "search",
  "searchQueries": [
    "python tutorial"
  ],
  "channelTabs": [
    "videos"
  ],
  "maxItems": 100,
  "language": "en",
  "region": "US",
  "includeChannelInfo": true,
  "timeoutSecs": 60,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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": [
        "python tutorial"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("datamule/youtube-data-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 = { "searchQueries": ["python tutorial"] }

# Run the Actor and wait for it to finish
run = client.actor("datamule/youtube-data-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 '{
  "searchQueries": [
    "python tutorial"
  ]
}' |
apify call datamule/youtube-data-scraper --silent --output-dataset

```

## MCP server setup

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