# YouTube Keyword Video Search Results Scraper (`datascraperes/youtube-keyword-search-scraper`) Actor

Search public YouTube videos by keyword without downloading media or using a paid API. Get ranked, deduplicated video rows with IDs, titles, channels, views, durations, thumbnails, query context, and explicit status items.

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

## Pricing

from $0.75 / 1,000 youtube video 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.
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

Find public YouTube videos from one or more keyword phrases and save a clean discovery snapshot. **The Actor returns video-level search rows without downloading media**, including the observed rank, title, channel, duration, views, thumbnail, and query that produced each result. Choose a fixed per-query limit or continue through search continuations until the global cap or source exhaustion.

### What this Actor does

This Actor searches the public YouTube video surface through yt-dlp and writes one structured Dataset item per delivered video. It is designed for keyword discovery, content research, SEO checks, creator discovery, and feeding selected video URLs into a separate transcript or channel workflow.

The contract is intentionally explicit: you choose the queries, a search mode, a maximum result count per query when using bounded mode, and a global video-row cap. Results can be deduplicated across queries while retaining the first query and rank where a video was found. The Dataset also receives a status item when a query has no results or cannot be completed, and the run summary records the mode plus processed, skipped, duplicate, empty, truncated, and failed-query counts.

### Use cases

- Discover public videos for a topic, product, competitor, or content brief.
- Build a bounded keyword-to-video dataset for SEO and editorial research.
- Send selected `videoUrl` values to a transcript Actor or a channel inventory workflow.

### How to use

1. Open the Actor in Apify Console.
2. Enter one or more keyword phrases, choose a search mode, and set the result limits.
3. Keep deduplication enabled unless you need one row for every query match.
4. Click **Start** and open the Dataset while results arrive or after the run completes.

### Input

`searchQueries` is required and accepts 1–20 non-empty phrases of up to 200 characters. Duplicate phrases are normalized and processed once. `searchMode` defaults to `bounded`: it requests a fixed number per query. In bounded mode, `resultsPerQuery` defaults to 10 and accepts 1–1,000 results per query. Set `searchMode` to `all` to follow available search continuations until the source ends or the global cap is reached; `resultsPerQuery` is ignored in this mode but remains available in the input for a consistent form. `maxItems` defaults to 100 and accepts 1–5,000 successful video rows across the complete run. `deduplicateResults` defaults to `true`; when enabled, the same video ID is emitted once across all queries.

This is a small valid input example:

```json
{
  "searchQueries": ["Google Developers Gemma"],
  "searchMode": "bounded",
  "resultsPerQuery": 5,
  "maxItems": 5,
  "deduplicateResults": true
}
```

### Output

Each successful Dataset item represents one public video result. `resultRank` is the position observed for that query during this run. `descriptionSnippet` is the visible search snippet, not a complete video description. `uploadDate`, `liveStatus`, counts, channel fields, and thumbnails are nullable because the public search response does not always provide them. `SUMMARY` in the default Key-value store contains the run-level counts and per-query status.

This is the complete Dataset item from a real successful execution of the current local Actor:

```json
{
  "status": "success",
  "searchQuery": "Google Developers Gemma",
  "resultRank": 1,
  "videoId": "O6499i4TFYA",
  "videoUrl": "https://www.youtube.com/watch?v=O6499i4TFYA",
  "title": "Gemma 4 and the AI Edge Gallery: On-Device AI Gets an Upgrade",
  "descriptionSnippet": "Olivier Lacombe, Director of Product Management for Gemma, sits down with Gus Martins and Alice Zheng to dig into what ...",
  "channelId": "UC_x5XG1OV2P6uZZ5FSM9Ttw",
  "channelName": "Google for Developers",
  "channelUrl": "https://www.youtube.com/channel/UC_x5XG1OV2P6uZZ5FSM9Ttw",
  "thumbnailUrl": "https://i.ytimg.com/vi/O6499i4TFYA/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLDiJq28U21881qx5KI1R4yl5eF9vA",
  "durationSeconds": 697.0,
  "viewCount": 8357,
  "uploadDate": null,
  "liveStatus": null,
  "scrapedAt": "2026-09-23T15:13:16Z",
  "errorCode": null,
  "errorMessage": null
}
```

For an empty query, the Dataset contains one item with `status: "empty"` and null video fields. If a query fails after delivering some rows, its error status item is saved and the run finishes as failed so partial data is not mistaken for a complete search. The Dataset can be exported in the formats supported by Apify, including JSON and CSV.

### Pricing

The Actor uses tiered pay-per-event pricing. A billable unit is one successful video result written to the default Dataset. The actual charge is made per result; the 1,000-result amount below is a comparison equivalent. Empty queries, failed-query status rows, unreturned results, the run summary, and storage or infrastructure usage are not Actor PPE events.

| Apify tier | Price per video result | Equivalent per 1,000 video results |
|---|---:|---:|
| FREE | $0.00100 | $1.00 |
| BRONZE | $0.00090 | $0.90 |
| SILVER | $0.00080 | $0.80 |
| GOLD | $0.00075 | $0.75 |
| PLATINUM | $0.00075 | $0.75 |
| DIAMOND | $0.00075 | $0.75 |

The Actor has one primary event, `youtube-video-result`, and does not configure a separate run-start or automatic Dataset-item event, so a successful video row is not double-charged. Apify resolves the applicable tier from the user's plan. Compute, storage, data transfer, and any proxy usage remain separate platform or account costs shown by Apify.

### Related Actors

| Actor | Best for |
|---|---|
| [YouTube Channel Videos Scraper | Shorts & Streams](https://apify.com/datascraperes/youtube-channel-scraper) | Inventory videos from known public channels when keyword discovery is not needed. |
| [YouTube Transcript Extractor - Timestamps](https://apify.com/datascraperes/youtube-transcript-scraper) | Extract complete available written captions from selected video URLs discovered by this Actor. |

### Limits and data quality

This is a search snapshot, not an exhaustive index of YouTube. YouTube can change ranking, visibility, available metadata, and continuation behavior between runs. A query may return fewer rows than requested, and the same query can produce a different order later. `maxItems` is a hard global cap in both modes; when reached, later queries are skipped and `SUMMARY.truncatedByMaxItems` is `true`. The `all` mode means “continue through the results exposed by the current search session”, not an official exhaustive index of every matching YouTube video.

The Actor does not download video, audio, images, or captions. It does not promise stable rankings, complete historical coverage, exact upload dates, or channel and playlist results. Search result metadata is what YouTube exposed at extraction time. A failed query is represented explicitly and causes a failed run; inspect both the Dataset and `SUMMARY` before treating a multi-query collection as complete.

### Frequently asked questions

#### Can I search several phrases in one run?

Yes. Add multiple values to `searchQueries`. Each query is tracked separately, and duplicate video IDs can be removed across queries with `deduplicateResults`.

#### Why did I receive fewer results than requested?

The public search response may contain fewer results, may omit entries, or may be limited by the global `maxItems` cap. Check `SUMMARY` for observed, emitted, duplicate, invalid, and truncated counts.

#### What is the difference between bounded and all mode?

Bounded mode asks YouTube for a fixed number of results per query, up to 1,000. All mode uses yt-dlp's continuation-aware search until the available source results are exhausted or the global `maxItems` cap is reached. It does not remove YouTube's ranking, visibility, rate-limit, or availability boundaries.

#### Does the Actor support YouTube date, duration, or popularity filters?

Not currently. The Actor exposes stable keyword and result-limit controls. YouTube's search URL filter tokens are internal and can change, so they are not accepted as raw user input.

#### Does the Actor return channels or playlists?

No. The contract is video-only. Use the channel Actor for a known channel inventory.

#### Does it return upload dates and full descriptions?

Only when the public search response supplies them. `descriptionSnippet` is a search snippet and `uploadDate` may be `null`; the Actor does not invent or silently derive missing values.

### Responsible use

Use the output only for purposes you are entitled to pursue. You are responsible for complying with YouTube's terms, applicable copyright and privacy requirements, and local law. Public visibility does not itself grant permission to republish media or personal information.

### Support

For an unexpected result, open an issue in the Actor's **Issues** tab with a run ID, a small reproducible query, the input limits, and any `errorCode`. Do not include credentials or private content.

# Actor input Schema

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

One or more keyword phrases. Duplicate phrases are processed once, preserving the first occurrence.

## `searchMode` (type: `string`):

Use a fixed result count per query, or continue through search continuations until the global cap or source exhaustion.

## `resultsPerQuery` (type: `integer`):

Maximum number of video results requested for each query in bounded mode, up to 1,000. Ignored in all mode, which is always bounded by maxItems.

## `maxItems` (type: `integer`):

Global cap on successful video rows across all queries. Status rows for empty or failed queries do not count toward this cap.

## `deduplicateResults` (type: `boolean`):

When enabled, a video found by multiple queries is emitted only once, keeping the first query and rank.

## Actor input object example

```json
{
  "searchQueries": [
    "Google Developers Gemma"
  ],
  "searchMode": "bounded",
  "resultsPerQuery": 5,
  "maxItems": 5,
  "deduplicateResults": true
}
```

# 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": [
        "Google Developers Gemma"
    ],
    "searchMode": "bounded",
    "resultsPerQuery": 5,
    "maxItems": 5,
    "deduplicateResults": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("datascraperes/youtube-keyword-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": ["Google Developers Gemma"],
    "searchMode": "bounded",
    "resultsPerQuery": 5,
    "maxItems": 5,
    "deduplicateResults": True,
}

# Run the Actor and wait for it to finish
run = client.actor("datascraperes/youtube-keyword-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": [
    "Google Developers Gemma"
  ],
  "searchMode": "bounded",
  "resultsPerQuery": 5,
  "maxItems": 5,
  "deduplicateResults": true
}' |
apify call datascraperes/youtube-keyword-search-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,datascraperes/youtube-keyword-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/4gbBEBvrGtXAS26gY/builds/YP8iSLjEPFTDtsaGB/openapi.json
