# YouTube Channel Search Scraper (`khadinakbar/youtube-channel-search-scraper`) Actor

Search public YouTube channels by keyword. Returns handle, subscribers, verification, and optional profile enrich. Cookieless, MCP-ready.

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

## Pricing

from $4.00 / 1,000 channel founds

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/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 Channel Search Scraper

Search public YouTube channels by keyword and receive one dataset row per unique channel. Each row returns channel name, handle, channel URL, subscriber text, video count, verification badge, and a search snippet. Optional About-page enrich adds description, total views, joined date, country, and public links. Cookieless HTTP, MCP-ready.

This Actor is built for lead-gen and creator-research workflows that start from a niche phrase and need a ranked list of public channel cards.

### Best fit for this Actor

- Discover public YouTube creators in a niche from a free-text keyword.
- Build a prospecting list of channel URLs and handles for outreach or CRM import.
- Feed MCP or spreadsheet workflows with one predictable channel card per match.

For mixed video, playlist, and channel SERP, start with [YouTube Search Scraper](https://apify.com/khadinakbar/youtube-search-scraper) and then return here when you only need channel cards. For a known channel URL or `@handle`, continue with [YouTube Channel Scraper](https://apify.com/khadinakbar/youtube-channel-scraper) after you pick a match. For public emails on a known channel, pass the URL to [YouTube Channel Email Extractor](https://apify.com/khadinakbar/youtube-channel-email-extractor).

### Practical scenario

A partnership manager types `personal finance`, keeps `maxResultsPerQuery` at `10`, and leaves `enrichProfiles` off. The run returns ranked public channel cards with name, handle, subscriber text, and channel URL. Handles that pass a subscriber floor are then passed to the known-URL channel scraper for About-page detail, or to the email extractor for contact harvest. An empty niche finishes `SUCCEEDED` with `VALID_EMPTY` and no `channel-found` charge.

### Quick start input

```json
{
  "searchQueries": ["personal finance"],
  "maxResultsPerQuery": 10,
  "maxResultsTotal": 50,
  "enrichProfiles": false
}
```

`searchQueries` accepts free-text keywords. Optional `searchUrls` accepts `youtube.com/results?search_query=...` pages; the Actor extracts the keyword and runs a channel-only search.

### Input reference

| Field | Type | What it controls |
|---|---|---|
| `searchQueries` | array (required) | Keywords such as `personal finance`. Prefills one example. |
| `searchUrls` | array | Optional YouTube results URLs that already contain `search_query`. |
| `maxResultsPerQuery` | integer | Unique channels saved per keyword. Default 10, max 200. |
| `maxResultsTotal` | integer | Hard cap across the whole run. Default 50, max 500. |
| `minSubscribers` / `maxSubscribers` | integer | Keep channels in a public subscriber band. Hidden counts stay in the set. |
| `verifiedOnly` | boolean | Keep cards that show YouTube's verified badge. Default false. |
| `country` / `language` | string | YouTube `gl` / `hl` plus residential geo. Defaults `US` / `en`. |
| `enrichProfiles` | boolean | Fetch About-page fields and bill `channel-enriched`. Default false. |
| `fallbackMode` | string | `auto` (default), `never`, or `always` for the managed recovery path. |
| `proxyConfiguration` | object | Residential proxy for the direct YouTube HTTP scrape. |

### What data you receive

One dataset item is one unique public YouTube channel card.

```json
{
  "type": "channel",
  "query": "personal finance",
  "position": 1,
  "source": "youtube",
  "channelId": "UCxxxxxxxxxxxxxxxxxxxxxx",
  "handle": "@examplefinance",
  "channelName": "Example Finance",
  "channelUrl": "https://www.youtube.com/@examplefinance",
  "handleUrl": "https://www.youtube.com/@examplefinance",
  "descriptionSnippet": "Weekly money explainers",
  "subscriberCount": 1200000,
  "subscriberCountText": "1.2M subscribers",
  "videoCount": 480,
  "videoCountText": "480 videos",
  "isVerified": true,
  "avatarUrl": "https://yt3.ggpht.com/example",
  "thumbnailUrl": "https://yt3.ggpht.com/example",
  "discoverySurface": "channel_search",
  "enriched": false,
  "description": null,
  "viewCount": null,
  "joinedDateText": null,
  "country": null,
  "keywords": null,
  "externalLinks": null,
  "rssUrl": null,
  "bannerUrl": null,
  "publicEmail": null,
  "scrapedAt": "2026-09-10T12:00:00.000Z"
}
```

| Field | Meaning |
|---|---|
| `channelName` + `handle` | Public display name and `@handle` |
| `subscriberCountText` / `videoCountText` | Search-card display strings |
| `isVerified` | Public verified badge on the card |
| `channelUrl` | Canonical public channel or handle URL |
| `enriched` | True after optional About-page enrich |
| `source` | `youtube` for direct HTTP, `managed-fallback` when recovery ran |

`OUTPUT` and `RUN_SUMMARY` in the default key-value store hold `outcome`, `itemsPushed`, and `chargedEventCounts`. Read those records to interpret empty datasets.

### Use through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/khadinakbar~youtube-channel-search-scraper/runs" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries":["personal finance"],"maxResultsPerQuery":10,"enrichProfiles":false}'
```

Download rows as JSON, CSV, Excel, or HTML from the Dataset tab.

### Use with AI agents through Apify MCP

> Find public YouTube channels for personal finance. Return channelName, handle, subscriberCountText, isVerified, and channelUrl. Keep the scope to public search cards. Read OUTPUT.outcome and itemsPushed for empty datasets. Cost signal: about $0.004 per found channel plus platform usage.

Connect via <https://mcp.apify.com>. Provenance is the public YouTube channel-search card; `scrapedAt` is collection time.

### Connect the workflow

- After you pick a channel, scrape the known URL with [YouTube Channel Scraper](https://apify.com/khadinakbar/youtube-channel-scraper).
- When you need public emails on that channel, continue with [YouTube Channel Email Extractor](https://apify.com/khadinakbar/youtube-channel-email-extractor).
- For a mixed video SERP on the same keyword, start with [YouTube Search Scraper](https://apify.com/khadinakbar/youtube-search-scraper).

### Pricing

This Actor uses Pay per event plus Apify platform usage. The live Pricing tab is the current source of truth for billing details.

- `apify-actor-start`: $0.00005 per run
- `channel-found`: **$0.004** per unique public channel row
- `channel-enriched`: **$0.006** per successful About-page enrich

A ten-channel prefill is about $0.04 in result events plus a few seconds of Apify platform usage. Empty niches and invalid keywords have no `channel-found` charge.

### How it works

1. Normalize keywords and optional results URLs, then collapse duplicates.
2. Request YouTube channel-only search over residential HTTP (InnerTube `ytInitialData` plus continuation).
3. When direct HTTP is unavailable, `fallbackMode=auto` uses the managed recovery path.
4. Charge `channel-found`, optionally enrich, then write the dataset item.

### Best results

- Provide a specific niche keyword such as `personal finance` rather than a channel URL.
- Start with `enrichProfiles` off for cheap discovery, then enrich the shortlist.
- Confirm `country` matches the market you care about so residential geo and YouTube `gl` stay aligned.
- Validate `OUTPUT.outcome` after the run: `COMPLETE` for a full set, `VALID_EMPTY` for a quiet niche.

### Builder's note

I found that YouTube's public channel-only search token already returns identity, subscriber display text, and verification on the card, which is enough for a lean discovery product. Keeping About-page fields behind an optional enrich lets agents run a cheap keyword scan first, then call the known-URL sibling only for channels that matter. Direct residential HTTP with a managed recovery path covers the days when YouTube HTML is consent-walled while staying on cookieless public data.

### Legal and responsible use

Use this Actor on public YouTube search data you are authorized to process, follow applicable law and YouTube's terms, and keep the output in your own compliance workflow. YouTube is a trademark of Google LLC. This independent Actor is not affiliated with, associated with, or endorsed by YouTube or Google.

Issues and feature requests: use the Actor Issues tab on Apify.

# Actor input Schema

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

Free-text YouTube search terms used to find public channels, for example 'personal finance' or 'fitness coach'. Each query is searched independently and unique channels are merged. Prefills one example query. This is not a channel URL or @handle — for a known channel use youtube-channel-scraper.

## `searchUrls` (type: `array`):

Optional youtube.com/results URLs that already contain a search\_query, for example 'https://www.youtube.com/results?search\_query=lofi'. The Actor extracts the keyword and runs a channel-only search. Defaults to empty. This is not a /channel or @handle URL.

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

Maximum unique channels saved for each search query after pagination. Example: 10. Range 1–200; default 10. This is not a global run cap — see maxResultsTotal.

## `maxResultsTotal` (type: `integer`):

Hard cap on unique channels billed and saved across every query in this run. Example: 50. Range 1–500; default 50. Extra matches after the cap are skipped. This is not the per-query limit.

## `minSubscribers` (type: `integer`):

Keep channels whose public subscriber count is at least this number, for example 10000. Default 0 (no floor). Channels that hide subscriber counts are kept. This is not a YouTube API quota.

## `maxSubscribers` (type: `integer`):

Keep channels whose public subscriber count is at most this number, for example 1000000. Default 0 means no ceiling. Hidden counts are kept. This is not a billing cap.

## `verifiedOnly` (type: `boolean`):

When true, keep only search cards that show YouTube's verified badge. Default false returns verified and unverified public channels. This does not confirm official brand ownership beyond the public badge.

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

Two-letter ISO country code used as YouTube geo context (gl) and residential proxy country, for example 'US' or 'GB'. Defaults to 'US'. This is not a language code — see language.

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

Two-letter ISO language code used as YouTube interface language (hl), for example 'en' or 'es'. Defaults to 'en'. This is not a country code — see country.

## `enrichProfiles` (type: `boolean`):

When true, each found channel is fetched for About-page fields (description, total views, joined date, country, links) and billed as one extra channel-enriched event. Default false returns search-card fields only. Leave off for cheap discovery.

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

Controls when the managed fallback runs after direct YouTube HTTP. 'auto' (default) uses residential YouTube first and falls back only if blocked. 'never' stays on direct HTTP. 'always' skips direct scraping. This is not a proxy group setting.

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

Proxy settings for the direct YouTube HTTP scrape. Residential proxies are enabled by default because datacenter IPs are blocked. Country follows the country field unless overridden. This is not the managed fallback.

## Actor input object example

```json
{
  "searchQueries": [
    "fitness coach",
    "indie game developer"
  ],
  "searchUrls": [
    "https://www.youtube.com/results?search_query=personal+finance"
  ],
  "maxResultsPerQuery": 10,
  "maxResultsTotal": 50,
  "minSubscribers": 0,
  "maxSubscribers": 0,
  "verifiedOnly": false,
  "country": "US",
  "language": "en",
  "enrichProfiles": false,
  "fallbackMode": "auto",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One row per unique public YouTube channel matched by keyword search.

## `output` (type: `string`):

Final outcome, itemsPushed, chargedEventCounts, and warnings.

## `runSummary` (type: `string`):

Per-run diagnostics including query counts, fallback usage, and billing counters.

# 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": [
        "personal finance"
    ],
    "maxResultsPerQuery": 10,
    "maxResultsTotal": 50,
    "country": "US",
    "language": "en"
};

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

# Run the Actor and wait for it to finish
run = client.actor("khadinakbar/youtube-channel-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 '{
  "searchQueries": [
    "personal finance"
  ],
  "maxResultsPerQuery": 10,
  "maxResultsTotal": 50,
  "country": "US",
  "language": "en"
}' |
apify call khadinakbar/youtube-channel-search-scraper --silent --output-dataset

```

## MCP server setup

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