# YouTube Search Scraper 🔎 (videos, views, rank tracking) (`tagadanar/youtube-search-scraper`) Actor

Every video YouTube ranks for your keywords, with rank position, exact views, upload date, length, channel and badges. Filters for upload date, length and popularity, any country. Turn on rank tracking to see each video's rank change since the last run. No API key, no login.

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

## Pricing

from $0.35 / 1,000 video 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

## YouTube Search Scraper: every video YouTube ranks for your keywords

Give it a list of keywords and get back every video YouTube shows for each one, in the order YouTube ranks them. Each row has the rank position, the exact view count, the upload date, the length, the channel (with its @handle and verified tick), the description snippet and the badges YouTube puts on the card (4K, CC, LIVE, New).

Turn on **rank tracking** and schedule the same input daily or weekly: every row then also says where that video ranked last time and how many places it moved. That is the part video-SEO people usually build by hand in a spreadsheet.

$0.50 per 1,000 videos, platform usage included. No API key, no Google Cloud quota, no login.

### What you get

One row per video per keyword:

| Field | Description |
| --- | --- |
| `query` | The keyword this row ranks for |
| `rank` | Position in YouTube's results for that keyword, 1 = top |
| `previousRank` | Rank in the previous tracked run (only with rank tracking on; null when the video is new) |
| `rankChange` | Places moved since the previous run, positive = moved up (only with rank tracking on; null when new) |
| `videoId`, `url` | The video |
| `title` | Video title |
| `channelName`, `channelId`, `channelUrl`, `channelHandle` | Who published it (`@handle` when the channel has one) |
| `isVerifiedChannel` | True when the channel has YouTube's verified tick |
| `viewCount` | Exact view count (YouTube's search card shows the full number, not "13K") |
| `liveViewers` | People watching right now, for live streams only (their `viewCount` is null) |
| `viewCountText` | The view count as YouTube printed it |
| `publishedTimeText` | Upload date as YouTube printed it, e.g. `3 months ago` |
| `publishedAtApprox` | An estimated ISO date computed from that text at capture time. YouTube rounds ("1 year ago"), so treat it as an estimate |
| `durationSeconds`, `durationText` | Length, e.g. `930` and `15:30` |
| `descriptionSnippet` | The description excerpt YouTube shows under the title |
| `thumbnailUrl` | Largest thumbnail on the card |
| `badges` | Card badges such as `LIVE`, `New`, `4K`, `CC`, `3 products` |
| `isLive`, `isUpcoming`, `scheduledStartAt` | Live now, or a scheduled premiere/stream and when it starts |
| `isShort` | Always false: this actor returns regular videos only (see below) |
| `capturedAt` | When the row was collected |

#### Example row (real, from a run on 2026-10-15)

```json
{
  "query": "home espresso machine review",
  "rank": 2,
  "previousRank": 4,
  "rankChange": 2,
  "videoId": "miNqgmVH9I0",
  "url": "https://www.youtube.com/watch?v=miNqgmVH9I0",
  "title": "Best Entry Level Espresso Machines 2025 – Our Top Picks for Beginners",
  "channelName": "Whole Latte Love",
  "channelId": "UCJwRmBtnYh_7Xs9oXUrLhZg",
  "channelUrl": "https://www.youtube.com/@Wholelattelovepage",
  "channelHandle": "@Wholelattelovepage",
  "isVerifiedChannel": true,
  "viewCount": 172059,
  "liveViewers": null,
  "viewCountText": "172,059 views",
  "publishedTimeText": "11 months ago",
  "publishedAtApprox": "2025-11-19T21:53:47.608Z",
  "durationSeconds": 930,
  "durationText": "15:30",
  "descriptionSnippet": "Looking for the best entry level espresso machine in 2025? We tested the top affordable espresso machines to find which ones ...",
  "thumbnailUrl": "https://i.ytimg.com/vi/miNqgmVH9I0/hq720.jpg?sqp=-oaymwEcCNAFEJQDSFXyq4qpAw4IARUAAIhCGAFwAcABBg==&rs=AOn4CLAceLLsmMyDSqB8kHdg_VBVJa2XJg",
  "badges": ["4K"],
  "isLive": false,
  "isUpcoming": false,
  "scheduledStartAt": null,
  "isShort": false,
  "capturedAt": "2026-10-15T21:53:47.608Z"
}
```

### Who uses this

- **Video-SEO agencies and YouTube creators.** Check where your videos and your competitors' rank for the keywords that matter, and watch the moves week after week with rank tracking.
- **Marketers and brand teams.** See which channels own a topic, how fresh the top results are, and how many views the top 20 videos pull, before you commission content or pick creators to sponsor.
- **Researchers.** Build a dataset of what YouTube surfaces for a set of queries, by country and language, with exact views and dates.
- **AI and agent builders.** Feed an agent the current top videos for a topic, then hand the video links to a transcript actor for the content itself.

### Input

| Field | What it does |
| --- | --- |
| `searchQueries` | Keywords, one per line (up to 200 per run). Each keyword is searched on its own |
| `maxResultsPerQuery` | Videos per keyword, 1 to 500 (default 50) |
| `uploadDate` | `any`, `hour`, `today`, `week`, `month`, `year`: YouTube's own upload-date filter |
| `duration` | `any`, `short` (under 4 min), `medium` (4 to 20 min), `long` (over 20 min), `under3` (under 3 min), `between3and20` (3 to 20 min) |
| `sortBy` | `relevance` (default) or `popularity`, the two orders YouTube's "Prioritize" menu offers |
| `country` | Country YouTube ranks for, e.g. `US`, `GB`, `DE`, `IN` (default `US`) |
| `language` | YouTube interface language, e.g. `en`, `fr`, `de` (default `en`) |
| `trackRankChanges` | Remember this run's ranks and add `previousRank` / `rankChange` to the next run with the same keywords and filters |

#### Top 30 videos for two keywords

```json
{
  "searchQueries": ["home espresso machine review", "best crm for small business"],
  "maxResultsPerQuery": 30
}
```

#### Weekly rank tracking, UK, long-form only

```json
{
  "searchQueries": ["espresso machine review", "best espresso machine under 500", "breville barista express review"],
  "maxResultsPerQuery": 50,
  "duration": "long",
  "country": "GB",
  "trackRankChanges": true
}
```

Schedule it once a week. The first run saves the baseline; every run after that fills `previousRank` and `rankChange`.

#### Most popular videos uploaded this month

```json
{
  "searchQueries": ["ai agents tutorial"],
  "maxResultsPerQuery": 100,
  "uploadDate": "month",
  "sortBy": "popularity"
}
```

### Pricing

| Event | Price |
| --- | --- |
| Video result | $0.0005 per video row, i.e. **$0.50 per 1,000 videos** |
| Run start | $0.001 per run |

Store discounts apply: Bronze $0.45, Silver $0.40, Gold $0.35 per 1,000 videos.

You only pay for rows delivered. A keyword YouTube has no videos for costs nothing beyond the run start. Platform usage (compute, proxies) is included, and rank tracking costs nothing extra.

### How rank tracking works

With `trackRankChanges` on, the actor keeps a small private store in your Apify account, named after your exact keywords and filters (country, language, upload date, length, sort). The next run with the same setup compares each video with its last position:

- `rankChange: 3` means the video moved up three places, `-2` means it dropped two.
- `previousRank: null` means it was not in last run's results (a newcomer, or it was beyond your `maxResultsPerQuery`).
- Changing any keyword or filter starts a fresh baseline, because it is a different results page. Changing `maxResultsPerQuery` does not.
- A keyword cut short (time limit, YouTube refusal) does not overwrite its saved ranking.

YouTube's results move around even between two runs a few minutes apart, especially past the first ten positions. Read rank changes as a trend over several runs rather than as a single verdict.

### Limits and honest caveats

- **Regular videos only, no Shorts.** This actor uses YouTube's "Videos" type filter, which leaves Shorts out. For Shorts, use the YouTube Shorts Scraper linked below.
- **YouTube decides how deep a search goes.** In our tests keywords ended somewhere between 100 and 400 videos. When YouTube stops before your `maxResultsPerQuery`, the run status says so for that keyword.
- **No "sort by upload date" or "sort by rating".** YouTube removed both orders from search; asking the API for them silently returns the relevance order, so they are not offered. Use the `uploadDate` filter to get recent videos.
- **Dates are relative.** YouTube only says "3 months ago". `publishedAtApprox` turns that into a date, which is exact to the hour for recent videos and approximate for older ones. It is computed for English, French, German, Spanish, Portuguese and Italian interfaces; for other languages it is null and `publishedTimeText` is still there.
- **Results are what an anonymous visitor in the chosen country sees.** No account, no watch history, no personalisation.

### FAQ

**How do I scrape YouTube search results without the YouTube Data API?**
Run this actor with your keywords. There is no API key and no daily quota. The Data API's search call costs 100 quota units per page and returns no view counts at all (they need a second call); this actor returns exact view counts straight from the results page.

**How do I track my video's ranking on YouTube for a keyword?**
Turn on `trackRankChanges`, put in your keywords and schedule the run daily or weekly. Filter the dataset on your `channelId` or `videoId` to follow your own videos.

**Can I search YouTube from another country?**
Yes. Pick the country (YouTube's location setting) and the interface language. Rankings differ by country, and rank tracking keeps a separate history for each.

**Does it return Shorts, channels or playlists?**
No, only regular videos. Shorts have their own actor (below).

**How many videos can I get per keyword?**
Up to 500, if YouTube has that many for the keyword. Broad one-word keywords often end sooner than long-tail ones.

**Is the view count exact?**
Yes. YouTube's search card carries the full number ("172,059 views"). Live streams show people watching now instead, in `liveViewers`.

**Can I get the transcript or comments of the videos?**
Pass the `url` column to the YouTube Transcript Scraper or the YouTube Comments Scraper below.

### Related YouTube actors

- [YouTube Channel Scraper](https://apify.com/tagadanar/youtube-channel-scraper): the videos and details of any channel.
- [YouTube Comments Scraper](https://apify.com/tagadanar/youtube-comments-scraper): the comments under any video.
- [YouTube Shorts Scraper](https://apify.com/tagadanar/youtube-shorts-scraper): Shorts, which this actor leaves out.
- [YouTube Channel Email Scraper](https://apify.com/tagadanar/youtube-channel-email-scraper): contact details of YouTube channels.
- [YouTube Transcript Scraper](https://apify.com/tagadanar/youtube-transcript-extractor): transcripts of any list of videos.

Keywords: YouTube search scraper, YouTube SERP scraper, YouTube rank tracker, YouTube keyword research, YouTube video scraper, YouTube search API alternative, YouTube views scraper.

# Changelog

This Actor's version history is a separate document: https://apify.com/tagadanar/youtube-search-scraper/changelog.md

# Actor input Schema

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

One keyword or phrase per line, typed exactly as a viewer would type it into YouTube's search box. Each keyword is searched separately and every row says which keyword it ranks for and at which position. Duplicates are removed.

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

Stop each keyword after this many videos. YouTube itself usually stops a search somewhere between 300 and 500 videos; when it ends earlier than this number, the run says so. You only pay for videos delivered.

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

Same as YouTube's 'Upload date' filter. 'Last hour' is still honoured by YouTube even though it left the filter menu.

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

Same as YouTube's length filter. YouTube's menu now shows 'Under 3 minutes' and '3 - 20 minutes'; the classic 'Under 4 minutes' and '4 - 20 minutes' buckets still work too.

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

Same as YouTube's 'Prioritize' choice. Popularity is YouTube's own view-weighted order. YouTube no longer sorts search results by upload date or rating; use 'Upload date' above to get recent videos.

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

The country YouTube ranks results for (its 'Location' setting).

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

YouTube's display language. It nudges the ranking and sets the language of texts such as '2 days ago'. Upload-date estimates are computed for English, French, German, Spanish, Portuguese and Italian.

## `trackRankChanges` (type: `boolean`):

Remember where each video ranked last time this exact set of keywords and filters was run, and add previousRank and rankChange to every row. Schedule the same input daily or weekly to watch rankings move. New videos get null. Nothing extra is charged.

## Actor input object example

```json
{
  "searchQueries": [
    "home espresso machine review",
    "best crm for small business"
  ],
  "maxResultsPerQuery": 30,
  "uploadDate": "any",
  "duration": "any",
  "sortBy": "relevance",
  "country": "US",
  "language": "en",
  "trackRankChanges": false
}
```

# Actor output Schema

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

One item per video per keyword in the 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 = {
    "searchQueries": [
        "home espresso machine review",
        "best crm for small business"
    ],
    "maxResultsPerQuery": 30
};

// Run the Actor and wait for it to finish
const run = await client.actor("tagadanar/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": [
        "home espresso machine review",
        "best crm for small business",
    ],
    "maxResultsPerQuery": 30,
}

# Run the Actor and wait for it to finish
run = client.actor("tagadanar/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": [
    "home espresso machine review",
    "best crm for small business"
  ],
  "maxResultsPerQuery": 30
}' |
apify call tagadanar/youtube-search-scraper --silent --output-dataset

```

## MCP server setup

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