# YouTube Search Scraper – Videos, Channels & Shorts (`arjun_code/youtube-search-scraper`) Actor

Unofficial YouTube search scraper for ranked channels, videos, and native Shorts. Get clean JSON with creator profiles, public emails when exposed, engagement metrics, descriptions, tags, chapters, country/language localization, and search rank—no YouTube API key. Not affiliated with YouTube.

- **URL**: https://apify.com/arjun\_code/youtube-search-scraper.md
- **Developed by:** [Arjun AI](https://apify.com/arjun_code) (community)
- **Categories:** Videos, Social media, Lead generation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $5.00 / 1,000 youtube search results

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

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

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

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

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

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

# README

### Search YouTube videos, channels, and Shorts by keyword

Turn YouTube search results into ranked, analysis-ready JSON. Choose one result type per run—channels, standard videos, or native Shorts—then control the keyword, YouTube region, interface language, ranking, date, duration, and result limit.

The Actor enriches only the final selected results. Channel searches return public channel profiles; video and Shorts searches return content metadata plus a compact channel identity. It does not silently expand channels into unrelated content, and it does not require a YouTube Data API key.

> This is an unofficial Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google. It collects publicly accessible metadata only.

### Ready-to-run examples

- [Find YouTube videos by keyword and region](https://apify.com/arjun_code/youtube-search-scraper/examples/find-youtube-videos-by-keyword-and-region)
- [Find YouTube channels by keyword](https://apify.com/arjun_code/youtube-search-scraper/examples/find-youtube-channels-by-keyword)
- [Find YouTube Shorts by keyword](https://apify.com/arjun_code/youtube-search-scraper/examples/find-youtube-shorts-by-keyword)

### What you can do

- Discover creators that rank for a topic or niche.
- Research standard videos by relevance, popularity, upload period, or duration.
- Search YouTube's native Shorts surface instead of guessing from video length.
- Compare localized search results across YouTube regions and interface languages.
- Export titles, descriptions, thumbnails, dates, engagement metrics, tags, categories, chapters, and basic creator data when publicly exposed.
- Build datasets for creator research, SEO, monitoring, market analysis, automation, or LLM workflows.

### Quick start

This input returns up to three standard videos for each keyword:

```json
{
  "searchQueries": [
    "AI automation",
    "faceless YouTube"
  ],
  "searchType": "videos",
  "maxResultsPerQuery": 3,
  "sortBy": "popularity",
  "uploadDate": "thisMonth",
  "duration": "3To20Minutes",
  "searchCountry": "US",
  "searchLanguage": "en"
}
```

### Input reference

| Field | Type | Default | Accepted values and behavior |
| --- | --- | --- | --- |
| `searchQueries` | `string[]` | `["AI automation"]` | 1–20 unique keyword phrases. The result limit applies separately to each phrase. |
| `searchType` | `string` | `videos` | `channels`, `videos`, or `shorts`. One run returns one consistent result type. |
| `maxResultsPerQuery` | `integer` | `3` | 1–25 results per keyword. |
| `sortBy` | `string` | `relevance` | `relevance` or `popularity` for channels and standard videos. Shorts use `relevance`. |
| `uploadDate` | `string` | `any` | `any`, `today`, `thisWeek`, `thisMonth`, or `thisYear` for standard videos. |
| `duration` | `string` | `any` | `any`, `under3Minutes`, `3To20Minutes`, or `over20Minutes` for standard videos. |
| `searchCountry` | `string` | `US` | YouTube content-region code used to localize ranking. It is not a strict channel-country filter. |
| `searchLanguage` | `string` | `en` | YouTube interface and localized-metadata language. It is not a strict spoken-language filter. |

The input form provides dropdowns for the YouTube region and language values supported by this Actor. Common examples are `US`, `GB`, `IN`, `DE`, `JP`, and `BR` for regions, and `en`, `es`, `de`, `ja`, `pt`, and `zh-CN` for languages. Use the form rather than maintaining a copied list of codes in your integration.

#### Which filters apply?

The Actor applies supported filters during YouTube search, before selecting results. Filters that do not apply to the selected result type are safely reset to that type's defaults.

| Result type | `sortBy` | `uploadDate` | `duration` |
| --- | --- | --- | --- |
| `channels` | `relevance` or `popularity` | `any` | `any` |
| `videos` | `relevance` or `popularity` | Supported | Supported |
| `shorts` | `relevance` | `any` | `any` |

`popularity` is YouTube's own server-side ranking. It is not a local numerical sort of the returned counters. `searchCountry` and `searchLanguage` influence YouTube's localized result context but do not guarantee that every result originates in that country or uses that spoken language.

### How it works

For each keyword, the Actor:

1. Requests the selected YouTube search surface with the effective filters.
2. Keeps up to `maxResultsPerQuery` ranked matches.
3. Enriches only those selected matches and streams them to the Dataset.

Channel enrichment reads the public channel profile and About data. Video and Shorts enrichment reads the content detail page and keeps only a six-field basic `channelProfile`; it does not make another request to the creator's About page.

The Actor searches by keyword. It does not crawl a channel's complete archive.

### Dataset output

The default Dataset contains one type-specific object per successful result:

- `channel` — channel identity, public statistics, history, branding, social links, and directly exposed emails.
- `video` — standard video metadata and compact channel identity.
- `short` — native Shorts metadata and compact channel identity.
- `searchStatus` — a free operational row for a keyword with no results, a failed search, or a maximum-charge stop.

Fields belonging to another result type are omitted instead of being added as unrelated `null` values.

#### Fields shared by successful results

| Field | Description |
| --- | --- |
| `recordType` | `channel`, `video`, or `short` |
| `detailStatus` | `complete` when enrichment finished; `basic` when useful search-level data was preserved after a detail failure |
| `detailError` | Detail failure message on a `basic` fallback |
| `title`, `url`, `description` | Public identity and description fields |
| `searchQuery` | Original keyword that produced the row |
| `searchRank` | One-based position inside that keyword's result set |
| `searchCountry`, `searchLanguage` | Effective YouTube localization context |
| `scrapedAt` | UTC ISO 8601 collection timestamp |

#### Channel fields

Channel rows can contain:

`avatarUrl`, `bannerUrl`, `channelId`, `channelName`, `channelHandle`, `channelUrl`, `subscriberCount`, `subscriberCountText`, `isVerified`, `totalViewCount`, `videoCount`, `joinedDate`, `joinedDateText`, `channelCountry`, `socialLinks`, and `emails`.

`emails` contains only addresses directly exposed in public page data. The Actor does not sign in, solve CAPTCHAs, or reveal protected business-email controls.

#### Video and Shorts fields

Content rows can contain:

`videoId`, `thumbnailUrl`, `durationSeconds`, `durationText`, `uploadDate`, `publishedAt`, `publishedTimeText`, `viewCount`, `viewCountText`, `likeCount`, `commentCount`, `liveStatus`, `availability`, `ageLimit`, `videoLanguage`, `tags`, `categories`, `chapters`, and `channelProfile`.

The compact `channelProfile` contains `channelId`, `channelName`, `channelHandle`, `channelUrl`, `subscriberCount`, and `isVerified`. It is not a full channel About profile.

### Real output examples

The records below are shortened from real runs. Counts and availability change over time.

#### Channel result

```json
{
  "recordType": "channel",
  "detailStatus": "complete",
  "title": "Automate AI Consulting",
  "url": "https://www.youtube.com/@AutomateAIConsulting",
  "channelId": "UCgugjY9T1jv41UcKmaZ5NLQ",
  "channelHandle": "@AutomateAIConsulting",
  "subscriberCount": 22000,
  "totalViewCount": 1292802,
  "videoCount": 377,
  "joinedDate": "2025-03-01",
  "channelCountry": "United States",
  "emails": [
    "collab@automateaiconsulting.com"
  ],
  "searchQuery": "AI automation",
  "searchRank": 3,
  "searchCountry": "US",
  "searchLanguage": "en"
}
```

#### Video result

```json
{
  "recordType": "video",
  "detailStatus": "complete",
  "videoId": "3mVWPk6BwVc",
  "url": "https://www.youtube.com/watch?v=3mVWPk6BwVc",
  "title": "Apple September 2026 Event - The 8 NEW Products!",
  "durationSeconds": 719,
  "durationText": "11:59",
  "uploadDate": "2026-08-28",
  "publishedAt": "2026-08-28T14:35:32Z",
  "viewCount": 102293,
  "likeCount": 1130,
  "commentCount": 62,
  "categories": [
    "Science & Technology"
  ],
  "channelProfile": {
    "channelId": "UCr6JcgG9eskEzL-k6TtL9EQ",
    "channelName": "ZONEofTECH",
    "channelHandle": "@ZONEofTECH",
    "subscriberCount": 1670000,
    "isVerified": true
  },
  "searchQuery": "apple event",
  "searchRank": 4
}
```

#### Shorts result

```json
{
  "recordType": "short",
  "detailStatus": "complete",
  "videoId": "ihbcOF4_2Ok",
  "url": "https://www.youtube.com/shorts/ihbcOF4_2Ok",
  "title": "The Lunch That Changed The Game #shorts #cooking #recipe",
  "durationSeconds": 39,
  "durationText": "39",
  "uploadDate": "2025-09-28",
  "publishedAt": "2025-09-28T14:48:32Z",
  "viewCount": 7496908,
  "likeCount": 171421,
  "commentCount": 1800,
  "channelProfile": {
    "channelId": "UCogoqwISeiIx0u4_7DqddPQ",
    "channelName": "Meatlicious",
    "channelHandle": "@Meatlicious_",
    "subscriberCount": 1030000,
    "isVerified": false
  },
  "searchQuery": "cooking",
  "searchRank": 1
}
```

#### No-result status

```json
{
  "recordType": "searchStatus",
  "status": "noResults",
  "searchQuery": "an extremely specific missing topic",
  "searchType": "videos",
  "errorCode": "NO_SEARCH_RESULTS",
  "errorMessage": "YouTube returned no matching public results for this keyword and filter combination.",
  "suggestion": "Try a broader keyword or less restrictive search filters."
}
```

### Pricing and result limits

This Actor uses pay-per-event pricing:

- **Actor start:** `$0.00005` per run.
- **Successful search result:** `$0.005` per `channel`, `video`, or `short` row—`$5.00` per 1,000 results.
- **Status rows:** `searchStatus` rows are free.

Example event charges before any configured discounts:

| Successful results | Event charge including one start |
| ---: | ---: |
| 1 | `$0.00505` |
| 5 | `$0.02505` |
| 100 | `$0.50005` |
| 500 | `$2.50005` |

The maximum requested scope is `number of keywords × maxResultsPerQuery`. With 20 keywords and 25 results per keyword, one run can request up to **500 successful search-result rows**. This is a ceiling, not a guarantee: YouTube can return fewer matches, and unavailable details can fall back to basic data.

The Actor respects the user's maximum run charge. The Actor's **Pricing** tab is the source of truth for active prices and discounts.

### Understanding missing fields

`detailStatus: "complete"` means the relevant detail request completed; it does not mean every optional field exists. A value can still be `null` or an array can be empty when YouTube hides it, the creator did not publish it, comments are disabled, the content is live or upcoming, or the selected response does not expose it.

`detailStatus: "basic"` means the Actor kept useful search-level metadata after detail enrichment failed. `detailError` explains the failure.

Subscriber counts are normalized from YouTube's public, sometimes abbreviated display value and can therefore be approximate. `subscriberCountText` preserves the displayed label when available. `joinedDate` uses `YYYY-MM-DD`; YouTube exposes a date, not a precise join time.

### Run summary

The default Key-Value Store record named `OUTPUT` provides run-level diagnostics without adding them to every result row. It includes effective filters, query counts and statuses, `resultsByQuery`, complete and basic result totals, Dataset item totals, concurrency, and whether the maximum charge was reached.

Use `searchQuery` plus `searchRank` to restore ranking order. Enrichment runs concurrently, so physical Dataset insertion order can differ from search order.

### Run with the Apify API

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/arjun_code~youtube-search-scraper/runs?token=$APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "searchQueries": ["AI automation"],
    "searchType": "videos",
    "maxResultsPerQuery": 3,
    "sortBy": "relevance",
    "uploadDate": "thisWeek",
    "duration": "under3Minutes",
    "searchCountry": "US",
    "searchLanguage": "en"
  }'
```

For synchronous testing, replace `/runs` with `/run-sync-get-dataset-items`. Use asynchronous runs for larger requests.

### Performance guidance

- Start with one keyword and 3–5 results to validate the result type and localization.
- Runtime grows with the number of keywords and selected results because each final match is enriched.
- Channel searches usually require a channel profile request; video and Shorts searches require a content-detail request.
- A broad keyword is usually faster and more reliable than a highly restrictive filter combination that produces no matches.

### Troubleshooting

#### The Dataset contains a `searchStatus` row

Read `status`, `errorMessage`, and `suggestion`. `noResults` usually means the keyword or filter combination was too narrow. `failed` means the search request did not complete. `chargeLimitReached` means the selected maximum run charge prevented more billable rows.

#### Search rank looks out of order in the Dataset

Detail requests finish concurrently. Sort by `searchQuery`, then `searchRank`; do not use Dataset insertion order as the ranking.

#### A field is empty even though the record is complete

Optional public metadata varies by result. Check the source page and the missing-field guidance above. The Actor does not invent hidden counters, contact details, tags, chapters, or country values.

### Related Actors

| Goal | Actor |
| --- | --- |
| Enrich known channel handles, IDs, or URLs | [YouTube Channel Details Scraper](https://apify.com/arjun_code/youtube-channel-details-scraper) |
| Get metadata for known video or Shorts URLs or IDs | [YouTube Video and Shorts Details Scraper](https://apify.com/arjun_code/youtube-video-details-scraper) |
| Export a channel's videos, Shorts, and streams | [YouTube Channel Content Scraper](https://apify.com/arjun_code/youtube-channel-content-scraper) |
| Export public comments and replies | [YouTube Comments Scraper](https://apify.com/arjun_code/youtube-comments-scraper) |

### FAQ

#### Does this use the YouTube Data API?

No. No YouTube API key or Google account is required.

#### Can region or language strictly filter creator nationality or spoken language?

No. They set YouTube's localized search context and influence ranking. They are not strict post-result filters.

#### Are Shorts detected only by duration?

No. Discovery uses YouTube's native Shorts search surface.

#### Can it reveal a protected business email?

No. Channel records include only email addresses directly exposed in public page data.

#### Can one run return channels and videos together?

No. Select one result type per run so the Dataset has a consistent schema. Run separate Tasks when you need multiple types.

### Support and responsible use

For unexpected data, open the Actor's **Issues** tab and include the run ID, input, affected keyword, result URL, and expected field. Do not post API tokens or private data.

Use the Actor only for lawful processing of publicly accessible information. You are responsible for complying with applicable laws, privacy requirements, YouTube's terms, and rules governing use of contact information.

# Actor input Schema

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

Add one keyword phrase per line. The result limit applies independently to each keyword.

## `searchType` (type: `string`):

Return one consistent result type per run.

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

Maximum results returned for each keyword. The Actor attempts to enrich every selected result from its relevant detail page.

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

Use YouTube's relevance or popularity ranking for channel and standard-video searches. Shorts automatically use Relevance.

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

Filter standard videos using YouTube's relative upload-date categories. Channel and Shorts searches automatically use Any time.

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

Filter standard videos using YouTube's duration categories. Channel and Shorts searches automatically use Any duration.

## `searchCountry` (type: `string`):

Select the YouTube content region used for localized search ranking. This is not a strict filter for a channel's country.

## `searchLanguage` (type: `string`):

Select the YouTube interface and localized metadata language used during search. This is not a strict filter for a video's spoken language.

## Actor input object example

```json
{
  "searchQueries": [
    "AI automation"
  ],
  "searchType": "videos",
  "maxResultsPerQuery": 3,
  "sortBy": "relevance",
  "uploadDate": "any",
  "duration": "any",
  "searchCountry": "US",
  "searchLanguage": "en"
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "searchQueries": [
        "AI automation"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("arjun_code/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": ["AI automation"] }

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

```

## MCP server setup

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