# YouTube Search Scraper - Bulk Keyword Results (`gganbukim/youtube-search-scraper`) Actor

Run many YouTube searches in one go and get every result as a row: title, video URL, channel, views, duration and approximate age. Sort by views, filter by view count, age or title text before billing. No API key and no Data API quota.

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

## Pricing

from $0.50 / 1,000 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/platform/actors/running/actors-in-store#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 Search Scraper — Bulk Keyword Results, Filtered Before You Pay

Run a list of search terms through YouTube and get every result as a row: title, URL,
channel, views, duration and approximate age, tagged with the query that produced it.

No API key. The YouTube Data API caps at 10,000 quota units a day and a single search
costs 100 of them — about 100 searches before you are locked out for the day. This has no
such ceiling.

### What it does well

- **Many queries per run** — each row carries its `query`, so one dataset covers a whole keyword set.
- **Filters run before billing** — minimum views, published-within, or a title fragment. **Filtered results cost nothing**, which is what makes a broad term safe to run.
- **Sorted output you can trust** — see the note below, because this is the part most search scrapers get quietly wrong.
- **Cheap and stable** — it reads search pages only, so it stays on datacenter proxies and rarely gets throttled.

### Input

```jsonc
{
  "queries": ["mechanical keyboard review", "best budget keyboard 2026"],
  "maxResultsPerQuery": 200,
  "sortBy": "views",
  "minViewCount": 10000
}
```

#### Every option

The same wording you see in the Apify console, with the JSON key for API and MCP callers.

| Option | What it does | Default |
|---|---|---|
| **Search queries** — `queries` *(required)* | One or more terms, exactly as you would type them into YouTube. Each becomes its own set of rows. | — |
| **Max results per query** — `maxResultsPerQuery` | Results arrive 20 per request, so this is the main cost control. | `100` |
| **Sort by** — `sortBy` | `relevance` is YouTube's own ranking. `views` biases which videos YouTube returns toward the most-watched, and the rows are then sorted by view count. | `"relevance"` |
| **Minimum views** — `minViewCount` | Drop results below this view count. Filtered results are not billed. | `0` |
| **Published within (days)** — `publishedWithinDays` | 0 disables. Search exposes only relative dates, so this is approximate to within a day. **Live streams carry no date at all and are dropped while this filter is on** — see the note below. Filtered results are not billed. | `0` |
| **Title contains any of** — `titleContains` | Case-insensitive fragments. A result is kept if its title contains any of them. | — |
| **Concurrency** — `concurrency` | Queries run in parallel. | `3` |
| **Proxy** — `proxyConfiguration` | Leave the default. Reads search pages only, so it stays on cheap datacenter proxies. | `{"useApifyProxy":true}` |

### Output

One row per result.

```json
{
  "query": "mechanical keyboard review",
  "rank": 1,
  "sortedBy": "views",
  "videoId": "8ta4dSQ-P9c",
  "url": "https://www.youtube.com/watch?v=8ta4dSQ-P9c",
  "title": "The Best Keyboard at Every Price",
  "viewCount": 1140040,
  "durationSeconds": 743,
  "publishedText": "1 year ago",
  "approxAgeDays": 365.3,
  "thumbnailUrl": "https://i.ytimg.com/vi/8ta4dSQ-P9c/hqdefault.jpg",
  "scrapedAt": "2026-08-20T09:14:02.113Z"
}
```

#### Every field

You are billed per result delivered, so here is everything a row can contain.

| Field | What it is |
|---|---|
| `query` | Which of your search terms produced this row. |
| `rank` | Position among the rows you were **given**, after filtering — not YouTube's internal ranking, which it does not publish. |
| `sortedBy` | The order that was applied, echoed back so a mixed dataset stays readable. |
| `videoId` | The 11-character id. |
| `url` | `watch?v=` URL. |
| `title` | Video title. |
| `viewCount` | Views, parsed from the display text. |
| `durationSeconds` | Length in seconds. |
| `publishedText` | YouTube's relative text, e.g. `"1 year ago"`. **Empty on live streams** — see the note on dates. |
| `approxAgeDays` | That text converted to days, accurate to about a day. |
| `thumbnailUrl` | Largest available thumbnail. |
| `scrapedAt` | When this run read the search. |

### Why there is no "sort by upload date"

Because it does not work, and shipping an option that silently does nothing is worse than
not shipping it.

YouTube's sort is a base64 filter parameter, and **a wrong or stale one does not return an
error — it returns relevance-ordered results.** A scraper that passes it and reports
success looks identical either way. So every option here was checked by asserting the
returned order was actually monotonic:

| Parameter | Meant to do | Result |
|---|---|---|
| `CAMSAhAB` | sort by views | **sorted** |
| `CAISAhAB` | sort by upload date | not sorted — came back in relevance order |
| `CAI=` | sort by upload date | not sorted |
| `CAESAhAB` | sort by rating | not sorted |
| `CAE=` | sort by rating | not sorted |

Only views survived, so only views is offered. For recency, use **Published within (days)**
— that filters on data actually present in every row.

There is a second half to this. Even with the working parameter, YouTube orders results
*within each page of 20* and then steps back up on the next continuation — measured over
60 results, descending in blocks with the order resetting at position 20 and again at 56.
So the parameter decides **which** videos come back, and this Actor sorts the rows it
received so the top of your dataset is genuinely the top. Reading the first 20 rows of a
100-row run without that would not be reading the 20 most-viewed.

### A note on dates

Search results carry **relative** publish text (`"3 weeks ago"`), never an exact
timestamp — the same limitation the channel listing has. `approxAgeDays` converts it for
filtering and sorting and is accurate to about a day. If you need exact publish dates, feed
the video URLs into the transcript Actor, whose per-video call returns `publishedAt`.

**Live streams carry no date at all.** A 24/7 radio stream or a running news channel has
no publish text to read — measured at 27 of 40 rows on two such queries. So while
`publishedWithinDays` is on, those rows are **dropped rather than delivered**: a filter
that cannot be evaluated has to exclude, or you would be paying for rows nothing checked
against the thing you asked for. The run summary counts them separately as `undateable`,
so you can see how many went that way. Turn the filter off and they come back.

### Who this is for

- **Video SEO specialists** — what already ranks for your terms, how long it is and how old — the gap is what is missing.
- **Content strategists** — search a niche, filter to the last 90 days, and see what nobody has covered.
- **Competitor researchers** — every channel ranking for your terms, in one pass.
- **Anyone building a pipeline** — every result carries a URL, so a keyword set becomes the input to a transcript or comments run.

### Common uses

- **Keyword research for video** — run a keyword set and see what already ranks, how long it is, and how old.
- **Content gap analysis** — search your niche, filter to the last 90 days, and see what nobody has covered.
- **Competitor discovery** — find every channel ranking for your terms.
- **Feeding the other Actors** — take the video URLs straight into a transcript, comments or sponsorship run.

### Pricing

Pay per **result delivered** — $0.50 per 1,000. Results removed by your filters and failed
queries **cost nothing**.

Starting a run costs $0.00002 — the platform's $0.00001 minimum, charged once per GB of
memory, and these Actors run on 2 GB. That is two thousandths of a cent per run.

### Other Actors in this family

Same engines, same billing, no account or API key on any of them.

**YouTube & video**

- [Download YouTube Subtitles in Bulk — SRT, VTT & Text](https://apify.com/gganbukim/youtube-transcript-scraper) — Bulk subtitles from videos, channels or playlists — text, SRT, VTT or RAG chunks.
- [Export YouTube Comments to CSV — Replies and Likes](https://apify.com/gganbukim/youtube-comments-scraper) — Every comment and reply thread, with likes, authors and creator flags.
- [List Every Video on a YouTube Channel — Export to CSV](https://apify.com/gganbukim/youtube-channel-scraper) — A channel's whole back catalogue plus a subscriber and RSS summary row.
- [Find YouTube Sponsors — Brand Deals, Codes & Links](https://apify.com/gganbukim/youtube-sponsorship-finder) — Which brands pay which creators, with the campaign link, the code and the timestamp.
- [Track Deleted YouTube Videos & Title Changes](https://apify.com/gganbukim/youtube-channel-monitor) — What a channel quietly changed: deleted videos, rewritten titles, view velocity.
- [YouTube Creator Email Finder & Sponsor Lookup](https://apify.com/gganbukim/youtube-creator-leads) — A channel list into leads: the published email, audience bands, and who already sponsors them.
- [Export a YouTube Playlist to CSV — Every Video](https://apify.com/gganbukim/youtube-playlist-scraper) — Any playlist as a table, with each video position in it.

**Search demand**

- [AnswerThePublic Alternative — Autocomplete Keyword API](https://apify.com/gganbukim/long-tail-keyword-scraper) — One seed into hundreds of real keywords from Google, YouTube and Amazon autocomplete.
- [Google Trends API — Today's Trending Searches, No Key](https://apify.com/gganbukim/google-trends-scraper) — Today's trending searches by country, with traffic bands and the news behind them.

**E-commerce**

- [Export Any Shopify Store's Products to CSV or JSON](https://apify.com/gganbukim/shopify-product-scraper) — Any Shopify catalogue: variants, SKUs, live prices, stock, images, collections.
- [New Shopify Product Alerts — Competitor Drop Tracker](https://apify.com/gganbukim/shopify-new-arrivals-monitor) — Only what a store launched since the last run. Scanning is free.

**Hiring**

- [Greenhouse, Lever & Ashby Job Scraper — No API Key](https://apify.com/gganbukim/ats-job-scraper) — Paste a company domain, get its open roles from Greenhouse, Ashby, Lever or SmartRecruiters.
- [Ghost Job Detector — Track Reposts, Closures & Edits](https://apify.com/gganbukim/job-lifecycle-monitor) — What changed on a careers page: opened, closed, quietly reposted, or a ghost job.

### FAQ

**Will I get blocked, or my account banned?** There is no account to ban — no login, no cookies, and no API quota to exhaust. Each query gets its own IP session, and the proxy tier escalates from datacenter to residential only when a query is actually throttled. YouTube signals throttling with an HTTP 200 and an error body rather than an error code, which is the trap that silently halves most scrapers' results; this one detects it by reason and rotates.

**Is it legal to scrape YouTube search results?** It reads the same public search pages a
browser requests, with no login and nothing bypassed, and the fields are facts about
videos — titles, view counts, durations — rather than the videos themselves. Public-data
scraping is broadly treated as lawful in the US; YouTube's Terms of Service are a separate
contract question. Not legal advice.

**Does it need a YouTube API key?** No, and that is most of the point. The Data API charges
100 quota units per search against a 10,000/day allowance.

**How many results can I get per query?** As many as YouTube will paginate. Set
**Max results per query** to control cost.

**How much does 1,000 results cost?** $0.50, plus $0.00002 for the run.

**Can I export the results to Excel or Google Sheets?** Yes. Every run's dataset downloads
as CSV, Excel, JSON, XML or RSS from the Storage tab, or straight from the API if you want
a live link a spreadsheet can pull.

**Can I connect it to Zapier, Make or n8n?** Yes — Apify publishes integrations for all
three, plus webhooks that fire when a run finishes.

**Do I need to write code?** No. Fill the form in the console and press Start. It is also
available over MCP so an AI agent can call it directly.

**Can I run it on a schedule?** Yes, via Apify Schedules, webhooks, or the API.

# Actor input Schema

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

One or more search terms, exactly as you would type them into YouTube. Each becomes its own set of rows, tagged with the query.

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

Results arrive 20 per request, so this is the main cost control.

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

Relevance is YouTube's own ranking. Views biases which videos YouTube returns toward the most-watched, and the rows are then sorted by view count so the top of your dataset really is the top. Upload date and rating are deliberately absent - see the README.

## `minViewCount` (type: `integer`):

Drop results below this view count. Filtered results are not billed.

## `publishedWithinDays` (type: `integer`):

0 disables. YouTube only exposes relative dates in search results ("3 weeks ago"), so this is approximate to within a day.

## `titleContains` (type: `array`):

Case-insensitive fragments. A result is kept if its title contains any of them. Leave empty to keep everything.

## `concurrency` (type: `integer`):

Queries run in parallel.

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

Leave the default. This actor only reads search pages, so it almost always stays on cheap datacenter proxies.

## Actor input object example

```json
{
  "queries": [
    "mechanical keyboard review"
  ],
  "maxResultsPerQuery": 100,
  "sortBy": "relevance",
  "minViewCount": 0,
  "publishedWithinDays": 0,
  "concurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per result, tagged with the query that produced it.

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

Counts for this run: queries completed, results delivered, results filtered out, queries that failed, and the proxy tier used.

# 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": [
        "mechanical keyboard review"
    ]
};

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

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

```

## MCP server setup

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