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

Scrape YouTube search results (videos, channels, playlists, movies) without cookies or the API. Title, channel, views, duration, publish date, thumbnails. Full filter support. HTTP + residential proxy primary, SerpApi last-resort fallback. MCP-ready.

- **URL**: https://apify.com/khadinakbar/youtube-search-scraper.md
- **Developed by:** [Khadin Akbar](https://apify.com/khadinakbar) (community)
- **Categories:** Videos, Social media, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, NaN bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 search results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use 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 Search Scraper — Videos, Channels, Playlists & Filters

Scrape **YouTube search results** at scale without cookies, login, or the rate-limited official Data API. Type one or more search queries, get back clean, structured JSON for every **video, channel, playlist, and movie** — with title, channel, view count, duration, publish date, thumbnails, and description.

This actor runs a **direct HTTP scrape over residential proxies** as its primary engine (fast and cheap), and automatically falls back to **SerpApi** only when YouTube blocks every retry — so a single hard block never turns into an empty run. The result is near-100% uptime without paying SerpApi prices on every request.

### When to use it

- **Content & SEO research** — find what ranks for a topic, the channels behind it, and how old the top results are.
- **Competitive monitoring** — track which videos surface for your brand or product keywords.
- **Market & trend analysis** — pull view counts and upload recency across hundreds of queries.
- **Lead and creator discovery** — surface channels and their metadata for outreach.
- **AI agents (MCP)** — narrow query in, structured JSON out, predictable per-result price.

Not the right tool for: a single video's **comments** (use `youtube-comments-scraper`), **transcripts** (`youtube-transcript-extractor`), **Shorts feeds** (`youtube-shorts-scraper`), or **channel email extraction** (`youtube-channel-email-extractor`).

### Output

One flat record per result. Example (video):

```json
{
  "type": "video",
  "query": "how to bake sourdough",
  "source": "youtube",
  "position": 1,
  "videoId": "dQw4w9WgXcQ",
  "title": "Beginner Sourdough Bread Recipe",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "channelName": "The Bread Code",
  "channelId": "UCxxxxxxxxxxxxxxxxxxxxxx",
  "channelUrl": "https://www.youtube.com/channel/UCxxxxxxxxxxxxxxxxxxxxxx",
  "viewCount": 1200000,
  "viewCountText": "1.2M views",
  "publishedTimeText": "2 years ago",
  "durationText": "12:34",
  "durationSeconds": 754,
  "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hq720.jpg",
  "description": "The simplest no-knead sourdough loaf...",
  "isLive": false,
  "isVerified": true,
  "badges": ["CC"],
  "subscriberCountText": null,
  "videoCountText": null,
  "playlistVideoCount": null,
  "scrapedAt": "2026-05-30T12:00:00.000Z"
}
````

Channel and playlist records use the same shape, filling `subscriberCountText` / `videoCountText` / `playlistVideoCount` and leaving video-only fields `null`. The `source` field tells you whether a record came from the direct scrape (`youtube`) or the fallback (`serpapi`).

| Field | Type | Notes |
|---|---|---|
| `type` | string | `video`, `channel`, `playlist`, or `movie` |
| `query` | string | The search query that produced this result |
| `source` | string | `youtube` (direct) or `serpapi` (fallback) |
| `position` | integer | 1-based rank within the query |
| `videoId` / `playlistId` / `channelId` | string|null | YouTube IDs |
| `title`, `url` | string|null | Result title and canonical URL |
| `channelName`, `channelUrl` | string|null | Owning channel |
| `viewCount`, `viewCountText` | integer / string|null | Parsed and raw views |
| `publishedTimeText` | string|null | e.g. "2 years ago" |
| `durationText`, `durationSeconds` | string / integer|null | Length |
| `thumbnailUrl`, `description` | string|null | Highest-res thumbnail, snippet |
| `isLive`, `isVerified` | boolean | Live broadcast / verified channel |
| `subscriberCountText`, `videoCountText`, `playlistVideoCount` | — | Channel/playlist fields |
| `scrapedAt` | string | ISO 8601 timestamp |

### Pricing

Pay-per-event, no monthly fee:

- **$0.003 per result** returned to the dataset.
- **$0.00005 actor start** (per run, scaled by RAM).
- **$0.01 per SerpApi fallback query** — charged **only** when a query falls back to the *managed* SerpApi engine. If you bring your own SerpApi key, this fee is never charged.

A typical run of 50 results costs about **$0.15**. The actor prints the maximum possible cost in the log and status message before charging anything, and a hard guard prevents it from ever billing past your `maxResultsPerQuery`.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `searchQueries` | string\[] | — (required) | One or more search terms |
| `maxResultsPerQuery` | integer | 50 | Cap per query (1–2000) |
| `resultType` | enum | all | all / video / channel / playlist / movie |
| `sortBy` | enum | relevance | relevance / uploadDate / viewCount / rating |
| `uploadDate` | enum | any | lastHour / today / thisWeek / thisMonth / thisYear |
| `duration` | enum | any | short (<4m) / medium (4–20m) / long (>20m) |
| `features` | string\[] | \[] | live, fourK, hd, subtitles, creativeCommons, threeSixty, vr180, threeD, hdr, location, purchased |
| `country` | string | US | gl geo + proxy country |
| `language` | string | en | hl interface/content language |
| `rawSpToken` | string | "" | Advanced: paste a raw `sp` filter token to override the structured filters |
| `serpApiKey` | string (secret) | "" | Your SerpApi key — makes fallback free of the managed fee |
| `fallbackMode` | enum | auto | auto (direct first, SerpApi last resort) / never / always |
| `proxyConfiguration` | object | residential | Residential proxies recommended |

#### Example input

```json
{
  "searchQueries": ["lofi hip hop", "react tutorial 2025"],
  "maxResultsPerQuery": 100,
  "resultType": "video",
  "sortBy": "viewCount",
  "uploadDate": "thisYear",
  "duration": "medium",
  "country": "US",
  "language": "en"
}
```

### How the dual engine works

1. **Direct HTTP (primary).** The actor requests YouTube's results page over a residential proxy, parses the embedded `ytInitialData`, and paginates using YouTube's internal continuation API. Full filter support is rebuilt locally into YouTube's own `sp` token, so any combination of sort, type, upload-date, duration, and feature filters works — exactly as if you set them in the YouTube UI.
2. **Session escalation.** If a request hits a consent wall, a 429, or a 403, the actor retires that proxy session and retries with a fresh one (several attempts).
3. **SerpApi fallback (last resort).** Only after direct attempts are exhausted, and only when `fallbackMode` allows it, the query is re-run through SerpApi's YouTube engine. Set `fallbackMode: "always"` to skip direct scraping entirely, or `"never"` to fail honestly instead of falling back.

If every query is blocked and nothing is collected, the run **fails honestly** with a clear message rather than reporting a misleading success with an empty dataset.

### Use via the API / MCP

The actor is MCP-ready: a narrow query input, a flat structured output under ~500 tokens per item, and a predictable per-result price. Point any MCP client at `https://mcp.apify.com?tools=khadinakbar/youtube-search-scraper` and the agent can search YouTube directly.

### FAQ

**Do I need a SerpApi key?** No. It is only used as a last-resort fallback. Without one, the actor still scrapes directly; if YouTube blocks every retry, the run fails honestly instead of falling back.

**Why residential proxies?** YouTube reliably blocks datacenter IPs. Residential proxies are enabled by default and strongly recommended.

**Can I sort by date or views?** Yes, via `sortBy`. Note YouTube only applies sort reliably on the first page; deep pagination drifts back toward relevance — a YouTube behavior, not an actor limitation.

**How many results per query?** Up to 2000. YouTube itself stops returning more after a few hundred for most queries.

### Related actors

- [YouTube Comments Scraper](https://apify.com/khadinakbar/youtube-comments-scraper) — Pull comments from videos surfaced by this search
- [YouTube Shorts Scraper — Views, Likes & Viral Score](https://apify.com/khadinakbar/youtube-shorts-scraper) — Drill into short-form metrics
- [YouTube Transcript Scraper — Bulk, Channel & Search](https://apify.com/khadinakbar/youtube-transcript-extractor) — Bulk transcripts from search results
- [YouTube Channel Email Extractor](https://apify.com/khadinakbar/youtube-channel-email-extractor) — Outreach to channels you discover
- [Google Trends Scraper](https://apify.com/khadinakbar/google-trends-scraper) — Pair search results with trend data

***

### Legal

This actor extracts publicly available data from YouTube search result pages. You are responsible for using the data in compliance with YouTube's Terms of Service, applicable laws (including copyright and data-protection regulations such as GDPR/CCPA), and for not collecting personal data without a lawful basis. This actor is not affiliated with, endorsed by, or sponsored by YouTube or Google.

# Actor input Schema

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

One or more free-text YouTube search terms, exactly as you would type them into the YouTube search bar (e.g. 'how to bake sourdough'). Each query is scraped independently and results are merged into one dataset. Defaults to a single example query. This is NOT a YouTube URL or video ID — for a specific video's comments or transcript use the dedicated youtube-comments-scraper or youtube-transcript-extractor actors.

## `maxResultsPerQuery` (type: `integer`):

Maximum number of results to return for each search query, across pagination. The actor stops paginating once this many items are collected. Defaults to 50; set higher for deep scrapes (each result is billed). NOT a global cap across all queries — it applies per individual query.

## `resultType` (type: `string`):

Restrict results to a single content type on YouTube. 'all' returns the natural mix YouTube serves (videos, channels, playlists). Choose 'video', 'channel', 'playlist', or 'movie' to narrow the search. Defaults to 'all'.

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

Ordering of the search results, mirroring YouTube's own sort options. 'relevance' is YouTube's default ranking; the others sort by upload recency, total view count, or rating. Defaults to 'relevance'. Note YouTube only sorts the first page reliably — deep pagination reverts toward relevance.

## `uploadDate` (type: `string`):

Restrict to videos uploaded within a recent time window, matching YouTube's 'Upload date' filter chips. 'any' applies no time filter. Only meaningful when results include videos. Defaults to 'any'.

## `duration` (type: `string`):

Restrict videos by length, matching YouTube's 'Duration' filter. 'short' is under 4 minutes, 'medium' is 4–20 minutes, 'long' is over 20 minutes. 'any' applies no duration filter and is the default.

## `features` (type: `array`):

Optional YouTube feature filters, applied together (e.g. \['hd','subtitles']). Mirrors YouTube's 'Features' filter chips such as Live, 4K, HD, Subtitles/CC, Creative Commons, 360°, VR180, 3D, HDR, Location, Purchased. Leave empty for no feature filtering (default). Combining many feature filters can sharply reduce result counts.

## `country` (type: `string`):

Two-letter ISO country code used as YouTube's geo context (gl parameter), affecting which regional results and trending content appear (e.g. 'US', 'GB', 'DE'). Also used as the residential proxy country. Defaults to 'US'. NOT a language code — see 'language' for that.

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

Two-letter ISO language code used as YouTube's interface/content language (hl parameter), affecting titles of localized metadata and relative date strings (e.g. 'en', 'es', 'de'). Defaults to 'en'. NOT a country code — see 'country' for that.

## `rawSpToken` (type: `string`):

Advanced override: a raw YouTube 'sp' filter token (the value after sp= in a filtered YouTube search URL). When set, it overrides the resultType/sortBy/uploadDate/duration/features fields entirely. Leave blank to let the actor build the token for you. Only for power users who copied a token from a complex YouTube filter URL.

## `serpApiKey` (type: `string`):

Optional SerpApi API key used ONLY as a last-resort fallback when YouTube blocks the direct HTTP scrape on every retry. Bring your own key from serpapi.com to make fallback free of the managed fallback fee. If left blank, the actor uses its managed SerpApi key (billed per fallback query). Stored as a secret and never logged.

## `fallbackMode` (type: `string`):

Controls when the SerpApi fallback engages. 'auto' (default) uses direct HTTP + residential proxy first and only falls back to SerpApi after all retries are exhausted. 'never' disables the fallback entirely (run fails honestly if YouTube blocks). 'always' skips direct scraping and uses SerpApi for every query (highest reliability, highest cost).

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

Proxy settings for the direct HTTP scrape. Residential proxies are strongly recommended and enabled by default — datacenter IPs are reliably blocked by YouTube. The country is auto-aligned to the 'country' field when not overridden here.

## Actor input object example

```json
{
  "searchQueries": [
    "lofi hip hop",
    "react tutorial 2025"
  ],
  "maxResultsPerQuery": 50,
  "resultType": "all",
  "sortBy": "relevance",
  "uploadDate": "any",
  "duration": "any",
  "features": [],
  "country": "US",
  "language": "en",
  "rawSpToken": "",
  "fallbackMode": "auto",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Scraped YouTube videos, channels, and playlists.

# 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 bake sourdough"
    ],
    "maxResultsPerQuery": 50,
    "country": "US",
    "language": "en"
};

// Run the Actor and wait for it to finish
const run = await client.actor("khadinakbar/youtube-search-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": ["how to bake sourdough"],
    "maxResultsPerQuery": 50,
    "country": "US",
    "language": "en",
}

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

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).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 bake sourdough"
  ],
  "maxResultsPerQuery": 50,
  "country": "US",
  "language": "en"
}' |
apify call khadinakbar/youtube-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=khadinakbar/youtube-search-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "YouTube Search Scraper — Videos, Channels & Playlists",
        "description": "Scrape YouTube search results (videos, channels, playlists, movies) without cookies or the API. Title, channel, views, duration, publish date, thumbnails. Full filter support. HTTP + residential proxy primary, SerpApi last-resort fallback. MCP-ready.",
        "version": "0.1",
        "x-build-id": "4br8jIfeKWDzRtK0s"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/khadinakbar~youtube-search-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-khadinakbar-youtube-search-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/khadinakbar~youtube-search-scraper/runs": {
            "post": {
                "operationId": "runs-sync-khadinakbar-youtube-search-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/khadinakbar~youtube-search-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-khadinakbar-youtube-search-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "searchQueries"
                ],
                "properties": {
                    "searchQueries": {
                        "title": "Search queries",
                        "type": "array",
                        "description": "One or more free-text YouTube search terms, exactly as you would type them into the YouTube search bar (e.g. 'how to bake sourdough'). Each query is scraped independently and results are merged into one dataset. Defaults to a single example query. This is NOT a YouTube URL or video ID — for a specific video's comments or transcript use the dedicated youtube-comments-scraper or youtube-transcript-extractor actors.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "maxResultsPerQuery": {
                        "title": "Max results per query",
                        "minimum": 1,
                        "maximum": 2000,
                        "type": "integer",
                        "description": "Maximum number of results to return for each search query, across pagination. The actor stops paginating once this many items are collected. Defaults to 50; set higher for deep scrapes (each result is billed). NOT a global cap across all queries — it applies per individual query.",
                        "default": 50
                    },
                    "resultType": {
                        "title": "Result type filter",
                        "enum": [
                            "all",
                            "video",
                            "channel",
                            "playlist",
                            "movie"
                        ],
                        "type": "string",
                        "description": "Restrict results to a single content type on YouTube. 'all' returns the natural mix YouTube serves (videos, channels, playlists). Choose 'video', 'channel', 'playlist', or 'movie' to narrow the search. Defaults to 'all'.",
                        "default": "all"
                    },
                    "sortBy": {
                        "title": "Sort by",
                        "enum": [
                            "relevance",
                            "uploadDate",
                            "viewCount",
                            "rating"
                        ],
                        "type": "string",
                        "description": "Ordering of the search results, mirroring YouTube's own sort options. 'relevance' is YouTube's default ranking; the others sort by upload recency, total view count, or rating. Defaults to 'relevance'. Note YouTube only sorts the first page reliably — deep pagination reverts toward relevance.",
                        "default": "relevance"
                    },
                    "uploadDate": {
                        "title": "Upload date filter",
                        "enum": [
                            "any",
                            "lastHour",
                            "today",
                            "thisWeek",
                            "thisMonth",
                            "thisYear"
                        ],
                        "type": "string",
                        "description": "Restrict to videos uploaded within a recent time window, matching YouTube's 'Upload date' filter chips. 'any' applies no time filter. Only meaningful when results include videos. Defaults to 'any'.",
                        "default": "any"
                    },
                    "duration": {
                        "title": "Video duration filter",
                        "enum": [
                            "any",
                            "short",
                            "medium",
                            "long"
                        ],
                        "type": "string",
                        "description": "Restrict videos by length, matching YouTube's 'Duration' filter. 'short' is under 4 minutes, 'medium' is 4–20 minutes, 'long' is over 20 minutes. 'any' applies no duration filter and is the default.",
                        "default": "any"
                    },
                    "features": {
                        "title": "Feature filters",
                        "type": "array",
                        "description": "Optional YouTube feature filters, applied together (e.g. ['hd','subtitles']). Mirrors YouTube's 'Features' filter chips such as Live, 4K, HD, Subtitles/CC, Creative Commons, 360°, VR180, 3D, HDR, Location, Purchased. Leave empty for no feature filtering (default). Combining many feature filters can sharply reduce result counts.",
                        "items": {
                            "type": "string",
                            "enum": [
                                "live",
                                "fourK",
                                "hd",
                                "subtitles",
                                "creativeCommons",
                                "threeSixty",
                                "vr180",
                                "threeD",
                                "hdr",
                                "location",
                                "purchased"
                            ],
                            "enumTitles": [
                                "Live",
                                "4K",
                                "HD",
                                "Subtitles/CC",
                                "Creative Commons",
                                "360°",
                                "VR180",
                                "3D",
                                "HDR",
                                "Location",
                                "Purchased"
                            ]
                        },
                        "default": []
                    },
                    "country": {
                        "title": "Country (gl)",
                        "type": "string",
                        "description": "Two-letter ISO country code used as YouTube's geo context (gl parameter), affecting which regional results and trending content appear (e.g. 'US', 'GB', 'DE'). Also used as the residential proxy country. Defaults to 'US'. NOT a language code — see 'language' for that.",
                        "default": "US"
                    },
                    "language": {
                        "title": "Language (hl)",
                        "type": "string",
                        "description": "Two-letter ISO language code used as YouTube's interface/content language (hl parameter), affecting titles of localized metadata and relative date strings (e.g. 'en', 'es', 'de'). Defaults to 'en'. NOT a country code — see 'country' for that.",
                        "default": "en"
                    },
                    "rawSpToken": {
                        "title": "Raw filter token (advanced)",
                        "type": "string",
                        "description": "Advanced override: a raw YouTube 'sp' filter token (the value after sp= in a filtered YouTube search URL). When set, it overrides the resultType/sortBy/uploadDate/duration/features fields entirely. Leave blank to let the actor build the token for you. Only for power users who copied a token from a complex YouTube filter URL.",
                        "default": ""
                    },
                    "serpApiKey": {
                        "title": "SerpApi key (optional fallback)",
                        "type": "string",
                        "description": "Optional SerpApi API key used ONLY as a last-resort fallback when YouTube blocks the direct HTTP scrape on every retry. Bring your own key from serpapi.com to make fallback free of the managed fallback fee. If left blank, the actor uses its managed SerpApi key (billed per fallback query). Stored as a secret and never logged."
                    },
                    "fallbackMode": {
                        "title": "SerpApi fallback mode",
                        "enum": [
                            "auto",
                            "never",
                            "always"
                        ],
                        "type": "string",
                        "description": "Controls when the SerpApi fallback engages. 'auto' (default) uses direct HTTP + residential proxy first and only falls back to SerpApi after all retries are exhausted. 'never' disables the fallback entirely (run fails honestly if YouTube blocks). 'always' skips direct scraping and uses SerpApi for every query (highest reliability, highest cost).",
                        "default": "auto"
                    },
                    "proxyConfiguration": {
                        "title": "Proxy configuration",
                        "type": "object",
                        "description": "Proxy settings for the direct HTTP scrape. Residential proxies are strongly recommended and enabled by default — datacenter IPs are reliably blocked by YouTube. The country is auto-aligned to the 'country' field when not overridden here.",
                        "default": {
                            "useApifyProxy": true,
                            "apifyProxyGroups": [
                                "RESIDENTIAL"
                            ]
                        }
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
