# YouTube Search Scraper (`socialdataapi/youtube-search-scraper`) Actor

YouTube Search Scraper — Extract YouTube search results including video titles, URLs, descriptions, channel names, views, upload dates, durations, and engagement metrics. Useful for keyword research, content discovery, competitor analysis, SEO, and video trend monitoring.

- **URL**: https://apify.com/socialdataapi/youtube-search-scraper.md
- **Developed by:** [Social Data API](https://apify.com/socialdataapi) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.49 / 1,000 results

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

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

## What's an Apify Actor?

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

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

## How to integrate an Actor?

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

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

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

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

# README

### YouTube Search Scraper — Views, Likes, Tags and Channel Data

YouTube Search Scraper runs a keyword search on YouTube and returns every video result as one row: title, watch URL, view count, likes, comment count, full description, tags, hashtags, duration, publish date, and the channel behind it — subscriber count, lifetime views, video count, and creation date. Every response is structured JSON — ready to pass directly to an LLM, index into a vector store, or feed a content-discovery pipeline. **No YouTube account or cookies needed.** Start with one keyword, then scale to a full topic list.

![YouTube Search Scraper output as JSON: titles, views, likes, tags and channel data](https://api.apify.com/v2/key-value-stores/WYkyEfqs10huyXYxr/records/a-youtube-search-hero.webp)

***

### What data can I extract with YouTube Search Scraper?

Using this YouTube Search Results API, you will be able to extract the following YouTube search data:

| 🎬 Title | 🔗 Watch URL | 🆔 Video ID | 📅 Publish date |
|---|---|---|---|
| ⏳ Duration | 👁️ View count | 👍 Like count | 💬 Comment count (when shown) |
| 📝 Full description | 🏷️ Tags (when provided) | #️⃣ Hashtags from the description | 🔃 Search rank |
| 📺 Channel name | 🔖 Channel handle | 📡 Channel URL and ID | 👥 Subscriber count |
| 📈 Channel lifetime views | 🎞️ Channel video count | 🙈 Hidden-subscriber flag | 🗓️ Channel creation date |
| 🔒 Members-only flag | 🖼️ Thumbnail URL | ⚠️ Reason a query gave no results | |

***

### How to use YouTube Search Scraper

Built for SEO and content researchers, creator-discovery teams, and AI builders: one run turns a list of keywords into a table of YouTube video search results, scraped with full video and channel detail attached to every row.

**Copy to your AI assistant:**

```text
`youtube-search-scraper` (Apify Actor ID: socialdataapi/youtube-search-scraper, https://apify.com/socialdataapi/youtube-search-scraper) is an Apify Actor that returns YouTube video search results as structured JSON: title, watch URL, view count, like count, comment count, full description, tags, hashtags, duration, publish date, and the channel behind each result (subscriber count, lifetime views, video count, creation date). Use it whenever someone needs YouTube search data without cookies, a login, or a browser - mapping who ranks for a topic, creator discovery and partnerships research, or feeding a content-monitoring agent. Call it from the Apify Store, the Apify API, or any Apify client library with your Apify token and a queries list. Inputs: queries (array of keywords, one search per line), maxResults (integer, 1-100000 per query, default 10), useFilters (boolean, default false; turns on order, eventType, publishedAfter/publishedBefore, videoDuration, videoDefinition, videoLicense and channelType), order (relevance/date/rating/title/videoCount/viewCount), eventType (live/upcoming/completed), videoDuration (any/short/medium/long), videoDefinition (any/high/standard), videoLicense (any/creativeCommon/youtube), channelType, regionCode, relevanceLanguage, safeSearch (moderate/none/strict), maxRetries, maxScrollRetries, proxyConfiguration. Every video result is one row charged as the row_result event. A query that returns nothing gets one free row with type "error" and the reason in error; it is never charged. maxResults is a target, not a promise - YouTube decides how deep a search goes, and videos that cannot be individually read drop out, with the shortfall logged.
```

#### YouTube Search Scraper MCP Server: use these Actors from ChatGPT, Claude or Cursor

Point your AI assistant at Apify and it can read YouTube search results on its own: keyword search, sort and date filters, and the channel data behind every result. No glue code, no scraping logic in your prompts.

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=socialdataapi/youtube-search-scraper"
```

#### Step by step

1. [Create a free Apify account](https://console.apify.com/sign-up).
2. Open [YouTube Search Scraper](https://apify.com/socialdataapi/youtube-search-scraper) in the Apify Store.
3. Type your search keywords, one per line.
4. Click **Start** (or send your API request).
5. Download or stream your results in JSON, CSV or Excel.

#### How to scale YouTube Search Scraper to bulk

Used for bulk YouTube keyword search, `queries` takes any number of keywords in one run: five queries are processed at the same time, and `maxResults` applies per query, not per run, so eight keywords at `maxResults: 100` is a target of 800 rows. Every row records the query that produced it in `input`, so one dataset fans back out per keyword afterwards. To track a topic over time, schedule the same run daily or weekly in Apify Console and diff on `id` to separate genuinely new uploads from back-catalogue videos resurfacing in the results.

***

### ✨ Features

YouTube Search Scraper fetches clean, structured video search results from YouTube ⚡, built to scrape YouTube search results at any scale. It's ideal for SEO monitoring, creator discovery and partnerships research, content format research, and AI topic-tracking agents — a YouTube keyword search tool for teams who don't want to provision a separate API quota. It extracts, per video:

- **Engagement numbers** – view count, like count and comment count on the same row as the result
- **Full video detail** – untruncated description, the video's own tag list and hashtags parsed from the description
- **Channel totals** – subscriber count, lifetime view count, video count and creation date, the kind of YouTube channel search data a plain search response does not carry
- **Verifiable ordering** – date, rating, title and view-count sorts are re-checked locally from the real values returned, not trusted blind from the response
- **Search rank** – each row's position in its query's result set, 0-indexed, the field a youtube video ranking scraper workflow reads first

⚡ A query with zero results gets a free error row instead of a silent, billed empty result.

***

### Use cases 💡

- **SEO strategist** — compares reach and relevance across twenty keywords in one run.
- **Content analyst** — tracks new uploads weekly with a published-after filter and view-count order.
- **Partnerships lead** — shortlists channels by subscriber count before reaching out.
- **Video producer** — studies tag vocabulary across top-ranking results for a format test.
- **AI engineer** — indexes titles, descriptions and tags into a RAG pipeline keyed on the query.

Building an AI agent? [Connect YouTube Search Scraper over MCP](#youtube-search-scraper-mcp-server-use-these-actors-from-chatgpt-claude-or-cursor) and let it call the actor on its own.

***

### Why choose this actor?

1. **Search plus full video and channel detail in one row.** No second lookup needed: view count, like count, comment count, tags and the publishing channel's subscriber, view and video counts all land on the same result row — a youtube search results scraper that does the follow-up calls for you.
2. **Four sort orders you can verify.** Date, rating, title and view count are re-sorted locally from the real values YouTube returns on every result, not trusted blind from the search response's own ordering.
3. **Zero-result queries never waste a charge.** A query that returns nothing gets one free, uncharged error row instead of a silent, billed empty result.

***

### ⬇️ Input

![Input fields of the YouTube Search Scraper](https://api.apify.com/v2/key-value-stores/WYkyEfqs10huyXYxr/records/a-youtube-search-input.webp)

Enter one or more search keywords in `queries` (not required — the schema ships a default query, so an untouched run still produces billable rows). Every other field is a filter, a resilience control, or a proxy choice, and each has a working default.

| Parameter | Required | Type | Description | Example value |
| ----- | ----- | ----- | ----- | ----- |
| `queries` | No | array | One search keyword per line. Default `["python programming"]`. | `["python programming", "fastapi tutorial"]` |
| `maxResults` | No | integer | Maximum results **per search query**. Minimum `1`, maximum `100000`, default `10`. | `50` |
| `useFilters` | No | boolean | Turns on `order`, `eventType`, `publishedAfter`/`publishedBefore`, `videoDuration`, `videoDefinition`, `videoLicense` and `channelType`. Default `false` — plain relevance search with none of those applied. | `true` |
| `order` | No | string | Result ordering, read only when `useFilters` is `true`: `relevance` (default), `date`, `rating`, `title`, `videoCount`, `viewCount`. `date`, `rating`, `title` and `viewCount` are re-sorted locally from the real values returned. `videoCount` has no meaning for video results and falls back to relevance. | `"viewCount"` |
| `eventType` | No | string | Restrict to live broadcasts, read only when `useFilters` is `true`: `""` (any, default), `live`, `upcoming`, `completed`. Only `live` is a real YouTube search filter; the other two are checked after the fact. | `"live"` |
| `publishedAfter` | No | string | RFC 3339 timestamp; only keep videos published on or after it. Checked after the fact, read only when `useFilters` is `true`. | `"2026-01-01T00:00:00Z"` |
| `publishedBefore` | No | string | RFC 3339 timestamp; only keep videos published on or before it. Checked after the fact, read only when `useFilters` is `true`. | `"2026-06-30T23:59:59Z"` |
| `videoDuration` | No | string | Length band, read only when `useFilters` is `true`: `any` (default), `short` (under 4 min), `medium` (4-20 min), `long` (over 20 min). A real YouTube search filter. | `"medium"` |
| `videoDefinition` | No | string | Video quality, read only when `useFilters` is `true`: `any` (default), `high` (a real filter), `standard` (no equivalent filter; ignored with a log note). | `"high"` |
| `videoLicense` | No | string | Video license, read only when `useFilters` is `true`: `any` (default), `creativeCommon` (a real filter), `youtube` (no equivalent filter; ignored with a log note). | `"creativeCommon"` |
| `channelType` | No | string | Channel type, read only when `useFilters` is `true`: `any` (default), `show` (no equivalent filter for keyword search; ignored with a log note). | `"any"` |
| `regionCode` | No | string | ISO 3166-1 alpha-2 country code that biases results toward that region. Default none (YouTube decides). | `"US"` |
| `relevanceLanguage` | No | string | ISO 639-1 language code that biases results toward that language. Default none (YouTube decides). | `"en"` |
| `safeSearch` | No | string | Content filtering level: `moderate` (default), `none`, `strict`. `strict` is enforced on the request; `moderate` and `none` currently return the same unfiltered result set YouTube serves by default. | `"moderate"` |
| `maxRetries` | No | integer | Retries for a single request after a block or server error. Default `3`. | `3` |
| `maxScrollRetries` | No | integer | Retries for an empty or failed pagination page before pagination stops for that query. Default `3`. | `3` |
| `proxyConfiguration` | No | object | Apify Proxy settings. Read when `useApifyProxy` is `true`; Apify Residential remains the terminal fallback either way. No default. | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"]}` |
| `debug` | No | boolean | Log every retry plus a line for every video and channel the run fetches, including failures. Default `false`. | `false` |

**On `maxResults`:** a target, not a promise. YouTube decides how deep a query actually goes, and individual videos drop out of extraction because they are private, region-locked or otherwise unextractable — the run simply omits them and logs the shortfall.

#### Free plan limits

Free plan: up to 20 videos per run (all queries together) and 200 per month; paid plans have no limit.

#### Example input

```json
{
  "queries": [
    "python programming",
    "fastapi tutorial"
  ],
  "maxResults": 50,
  "useFilters": true,
  "order": "viewCount",
  "videoDuration": "medium",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  },
  "debug": false
}
```

***

### ⬆️ Output

![Output fields of the YouTube Search Scraper](https://api.apify.com/v2/key-value-stores/WYkyEfqs10huyXYxr/records/a-youtube-search-output.webp)

Every video is one typed, normalized JSON item with the same 27 fields on every run; a field YouTube genuinely does not provide stays absent rather than a made-up zero. Rows are written to the dataset as each query finishes, so a stopped run keeps everything collected up to that point, and you can export as JSON (default), CSV or Excel. A query that returns nothing comes back as one free error row in the same dataset.

Two keys are **omitted from a row entirely rather than nulled** when they do not apply: `commentsCount` when comments are off, and `keywords` when the video carries none. Read with `.get()` rather than assuming either key is present.

#### Example output

A video result row, and an error row for a query that returned nothing:

```json
[
  {
    "title": "FastAPI in 100 Seconds",
    "type": "video",
    "channelName": "Fireship",
    "date": "2026-06-11T15:02:41Z",
    "text": "FastAPI is a modern Python web framework built on Starlette and Pydantic. Build a production REST API with automatic OpenAPI docs, dependency injection and async request handling.\n\n#python #fastapi #webdev",
    "thumbnailUrl": "https://i.ytimg.com/vi/7t2alSnE2-I/maxresdefault.jpg",
    "order": 0,
    "input": "fastapi tutorial",
    "hashtags": ["#python", "#fastapi", "#webdev"],
    "channelId": "UCsBjURrPoezykLs9EqgamOA",
    "channelUrl": "https://www.youtube.com/@fireship",
    "channelUsername": "fireship",
    "numberOfSubscribers": 3940000,
    "channelViewCount": 621904113,
    "channelVideoCount": 712,
    "hiddenSubscriberCount": false,
    "channelCreatedAt": "2017-04-06T15:08:16Z",
    "id": "7t2alSnE2-I",
    "url": "https://www.youtube.com/watch?v=7t2alSnE2-I",
    "viewCount": 1284366,
    "likes": 48219,
    "commentsCount": 1372,
    "commentsTurnedOff": false,
    "duration": "00:02:41",
    "keywords": ["fastapi", "python", "rest api", "async", "pydantic"],
    "isMembersOnly": false,
    "scrapedAt": "2026-07-25T09:14:02Z",
    "runId": "kR8mQzVx2LpNc7Yd4"
  },
  {
    "type": "error",
    "input": "zqxjv nonexistent phrase",
    "error": "no results returned",
    "scrapedAt": "2026-07-25T09:14:09Z"
  }
]
```

Rows with `type: "error"` are not charged — only `type: "video"` rows are.

***

### How does it work?

It requests the same search, video and channel data a signed-out browser would, as structured data rather than rendered markup, then normalises it onto the row shape above — a youtube search data extractor that reads only what any visitor can already see. Filtering happens server-side at YouTube wherever YouTube exposes a real search parameter for it; a handful of options (`publishedAfter`/`publishedBefore`, `eventType: upcoming`/`completed`) have no server-side equivalent and are checked locally against each video's own details instead, which is why those two can return fewer rows than requested. The one post-fetch step that changes row order rather than row count is a re-sort, applied when you choose `order: date`, `rating`, `title` or `viewCount`, because YouTube blends relevance back into its own ordering. Requests retry on a block or server error with a backoff between attempts, and the run moves to a residential proxy address when it needs to.

Because the actor reads YouTube's structured data rather than scraping rendered HTML, a YouTube front-end redesign does not change your field names. Only publicly visible data is collected — no account, cookie or session is used.

***

### How do you integrate YouTube Search Scraper into your stack?

It works with what you already use: the REST API from any language, MCP for AI agents, and no-code tools.

#### Using YouTube Search Scraper with the Apify API

Call the Actor's run-sync-get-dataset-items endpoint with your Apify token and a `queries` list, or use the `apify-client` package for your language to start the run and read back the dataset items. Works in Python, Go, Ruby, Node.js, cURL — any language that can make an HTTP request.

#### MCP integration for AI agents

Add the Apify MCP server to your AI client with one command:

```bash
claude mcp add --transport http apify "https://mcp.apify.com?tools=socialdataapi/youtube-search-scraper"
```

This registers it as a tool your agent can call directly: the agent finds it through the server's search, adds it and runs it, then reads the search results back as JSON. Ask for "the ten most-viewed videos about this topic published this month" and it does the rest. It works in Claude Code, Cursor and Windsurf.

#### No-code tools (n8n, Make, LangChain)

**n8n:** add the Apify node and choose "Run an Actor", or use the HTTP Request node pointed at the run-sync endpoint with your token; a Filter node on `type` drops the error rows.

**Make:** use the Apify app's "Run an Actor" module, then "Get Dataset Items" so a weekly keyword sweep can feed a Google Sheets or Airtable step directly.

**LangChain:** install `langchain-apify` and wrap the actor with `ApifyActorsTool()` so your agent can fetch YouTube search results on demand; the response is already typed JSON, so no output parser is needed.

***

### Is it legal to scrape YouTube search results?

Collecting publicly listed video metadata is broadly treated as permissible where no authentication is bypassed, and this actor reads only signed-out search results and public video and channel data — no account, no cookie, no session. What comes back is content metadata, not personal data: video titles, view counts, like counts, durations, tags, publish timestamps, channel names and channel statistics — no viewer information, no comment authors, no watch history and no contact details. GDPR: scraping public data is generally permissible, but storing and using it must comply with local data-protection law. One nuance: an individual creator's channel identifies a person, so `channelName` and `channelUsername` on that row can be personal data even though the same keys on a broadcaster's or a company's channel row are not. Consult legal counsel if your use case involves bulk storage of personal data.

***

### Frequently asked questions

#### What fields does YouTube Search Scraper return?

The five most used are `title`, `url`, `viewCount`, `channelName` and `date`. See [the data fields section](#what-data-can-i-extract-with-youtube-search-scraper) for the full list.

#### Does YouTube Search Scraper require a YouTube account or login?

No. It runs signed out end to end — no YouTube account, no Google login, no Google Cloud project, no API key. The only credential you need is your Apify token.

#### What happens when a query returns zero results?

The query still produces exactly one row — an uncharged row with `type: "error"`, the `input` that produced it, a `scrapedAt` stamp and an `error` message carrying the last thing the run saw — and the run continues with your remaining queries. Filter with `row.get("type") != "error"` to keep results only.

#### Can I search multiple YouTube keywords at once?

Yes. `queries` is a list — one keyword per line, processed up to five at a time for bulk YouTube keyword search workloads.

#### Does YouTube Search Scraper work with Claude, ChatGPT, and other AI agent tools?

Yes — YouTube Search Scraper is available as an MCP tool. Register it in Claude Code, Cursor or Windsurf with `claude mcp add --transport http apify "https://mcp.apify.com?tools=socialdataapi/youtube-search-scraper"`. Any agent that can call an HTTP endpoint can use the REST API directly.

#### How does YouTube Search Scraper compare to the YouTube Data API?

The YouTube Data API v3 is public, documented and does support search, so the difference is what you provision and what one result row contains. It needs a Google Cloud project and an API key, charges search against a daily quota, and returns only identifiers and snippets — engagement counts and channel totals need a second lookup per video. YouTube Search Scraper needs only your Apify token, has no Google quota, and puts `viewCount`, `likes`, `commentsCount`, `keywords`, `text` and the channel's own totals on the same row as the search result — a youtube video search api alternative, and more broadly a youtube search api alternative, for search-heavy workloads that would exhaust that daily budget. Verify current scopes, terms and quota costs against Google's published documentation before committing to either route.

#### Does YouTube Search Scraper return data in a format LLMs can use directly?

Yes. Each row is typed, normalized JSON with consistent field names, so there is no HTML parsing and no selectors. Pass it straight to an LLM, index it into a vector store, or feed it to an agent tool — no transformation step.

#### What happens when YouTube changes its layout or updates its anti-bot system?

The actor is maintained, and when YouTube changes, it is updated so your calls keep returning the same JSON fields with no changes on your end.

#### Can I use YouTube Search Scraper without managing proxies or browser infrastructure?

Yes. Retries, backoff and the move to a residential proxy address all run on the actor's side, and no browser is needed. You send one request and get structured JSON back, with no proxy accounts and no maintenance.

#### Which YouTube search fields work best for AI training data and RAG indexing?

For RAG indexing: `text`, `title` and `keywords` carry the most descriptive content per row and chunk cleanly, with `url` as the citation anchor and `input` as a natural partition key. For training data: `viewCount`, `likes`, `commentsCount`, `duration`, `order` and `date` are the most structurally consistent fields, arriving as integers, a fixed-format duration string and an ISO timestamp rather than display text.

#### Are results identical between two runs of the same query?

No, and that is YouTube rather than the actor. Two identical searches minutes apart can return partly different videos in a partly different order, so a run-to-run difference is not evidence that a filter did or did not apply. What is stable is the schema: the same key names and types every run, with `scrapedAt` and `input` stamped on each row so any snapshot carries its own provenance.

***

### Related scrapers

| Scraper Name | What it extracts |
| ----- | ----- |
| YouTube Video Details Scraper | Deep metadata for a single YouTube video |
| YouTube Playlist Scraper | A playlist's full contents with publish dates and video stats |
| YouTube Channel Finder | Channels resolved from names and handles |
| YouTube Community Posts Scraper | Posts from a channel's community tab |
| YouTube Shorts Scraper | Short-form videos with creator lead enrichment |
| Facebook Video Search Scraper | Video search results for the same keyword on Facebook |

***

### Your feedback

Found a bug, or missing a field YouTube exposes that is not in the output? Open an issue on the actor's Issues tab. Reports that include the exact query and the input JSON you ran are the fastest to reproduce and fix.

# Actor input Schema

## `queries` (type: `array`):

One search keyword per line, e.g. 'python programming' or 'best cooking channels'. Each line is searched independently and every result is a video.

## `maxResults` (type: `integer`):

Maximum number of videos to return per search query. Each video is extracted in full (views, likes, comment count, tags, full description, channel data), so higher values and more queries take longer and use more proxy bandwidth. Free plan: up to 20 videos per run (all queries together) and 200 per month; paid plans have no limit.

## `useFilters` (type: `boolean`):

Turn on the filters below (sort order, event type, published after/before, duration, definition, license, channel type). When off, results use YouTube's default relevance order with none of those filters applied.

## `order` (type: `string`):

Result ordering, applied only when advanced filters are enabled. 'Upload date', 'Likes', 'Title' and 'View count' are re-sorted here from the real values returned, so those four are verifiable and deterministic. 'Relevance' keeps YouTube's own ordering. 'Video count' has no meaning for video results and falls back to relevance with a log note.

## `eventType` (type: `string`):

Restrict results to live broadcasts, applied only when advanced filters are enabled. 'Live now' is a real YouTube search filter. 'Upcoming' and 'Completed live stream' are checked after the fact against each video's own details, so fewer than the requested number of results is possible with either.

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

Only keep videos published on or after this moment. RFC 3339 timestamp, e.g. '2022-01-01T00:00:00Z'. Checked after the fact against each video's own publish date, so fewer than the requested number of results is possible. Applies only when advanced filters are enabled.

## `publishedBefore` (type: `string`):

Only keep videos published on or before this moment. RFC 3339 timestamp, e.g. '2022-12-31T23:59:59Z'. Checked after the fact against each video's own publish date, so fewer than the requested number of results is possible. Applies only when advanced filters are enabled.

## `videoDuration` (type: `string`):

Only include videos in this length band. Applied as YouTube's own search filter; only applies when advanced filters are enabled.

## `videoDefinition` (type: `string`):

Only include videos of this quality. 'High definition' is a real YouTube search filter; 'Standard definition' has no equivalent filter and is ignored with a log note. Applies only when advanced filters are enabled.

## `videoLicense` (type: `string`):

Only include videos under this license. 'Creative Commons' is a real YouTube search filter; 'Standard YouTube license' has no equivalent filter and is ignored with a log note. Applies only when advanced filters are enabled.

## `channelType` (type: `string`):

Restrict results by channel type. 'Show' has no equivalent YouTube search filter for keyword search and is ignored with a log note. Applies only when advanced filters are enabled.

## `regionCode` (type: `string`):

ISO 3166-1 alpha-2 country code that biases search results toward that region, e.g. 'US'. Leave blank to let YouTube decide.

## `relevanceLanguage` (type: `string`):

ISO 639-1 language code that biases search results toward that language, e.g. 'en'. Leave blank to let YouTube decide.

## `safeSearch` (type: `string`):

Content filtering level applied to search results.

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

How many times to retry a single request after a block or server error before giving up on that step.

## `maxScrollRetries` (type: `integer`):

How many times to retry an empty or failed search results page before stopping pagination for that query.

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

Your proxy choice is used first. Note: full per-video extraction is bot-walled on datacenter IPs (measured: 0 rows, 'Sign in to confirm you're not a bot'), so if the proxy you select gets blocked, the run escalates to Apify RESIDENTIAL, which is the only exit confirmed to return rows. The log states which proxy each request used.

## `debug` (type: `boolean`):

Log additional detail: every retry, plus a line for every video and channel the run fetches, including failures. Useful for troubleshooting.

## Actor input object example

```json
{
  "queries": [
    "python programming"
  ],
  "maxResults": 10,
  "useFilters": false,
  "order": "relevance",
  "eventType": "",
  "publishedAfter": "",
  "publishedBefore": "",
  "videoDuration": "any",
  "videoDefinition": "any",
  "videoLicense": "any",
  "channelType": "any",
  "regionCode": "",
  "relevanceLanguage": "",
  "safeSearch": "moderate",
  "maxRetries": 3,
  "maxScrollRetries": 3,
  "debug": false
}
```

# Actor output Schema

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

All scraped items in the Actor's default dataset.

# 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 = {
    "queries": [
        "python programming"
    ]
};

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

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

```

## MCP server setup

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