# YouTube Scraper (`mlg14/youtube-search-scraper`) Actor

Search public YouTube videos, Shorts, channels, playlists, and movies by keyword. Export titles, IDs, links, channels, view counts, durations, and thumbnails with filters and pagination.

- **URL**: https://apify.com/mlg14/youtube-search-scraper.md
- **Developed by:** [MLG Data](https://apify.com/mlg14) (community)
- **Categories:** Developer tools, Automation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.00 / 1,000 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?

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 Scraper

Scrape YouTube search results for videos, Shorts, channels, playlists, and movies, then export YouTube data to CSV, JSON, or Excel. This YouTube search API alternative turns a keyword and optional search filters into a structured dataset of public results.

Enter a query, choose the result type, and set a maximum result count. The scraper follows search continuation pages, removes duplicate result IDs, and saves one row per result. It reads the public search surface; it does not download videos, read private account data, or require a key from you.

### What data can you extract from YouTube?

Each row describes one public search result. The same field names appear across result types so exports can be joined or compared. A field is `null` when that type of result or the search response does not supply it. Thumbnails are an array because a result can provide several sizes.

| Field | Description | Example |
|---|---|---|
| `type` | Result kind | `"video"` |
| `query` | Search phrase that produced the row | `"python tutorial"` |
| `position` | Position among saved, unique results | `1` |
| `videoId` | Identifier for a video, Short, or movie | `"_uQrJ0TkZlc"` |
| `channelId` | Channel identifier, when supplied | `"UC..."` |
| `playlistId` | Playlist identifier, when applicable | `"PL..."` |
| `url` | Direct link to the result | `"https://www.youtube.com/watch?v=_uQrJ0TkZlc"` |
| `title` | Displayed title | `"Python Full Course for Beginners"` |
| `channelTitle` | Displayed channel name | `null` for some Shorts |
| `channelUrl` | Link to the public channel | `"https://www.youtube.com/channel/UC..."` |
| `description` | Search result snippet | `"Learn Python..."` |
| `viewCountText` | View count as displayed by the source | `"49,152,339 views"` |
| `viewCount` | Parsed numeric view count | `49152339` |
| `publishedTimeText` | Relative publication time in search | `"7y ago"` |
| `lengthText` | Displayed video length | `"6:14:07"` |
| `durationSeconds` | Length converted to seconds | `22447` |
| `thumbnail` | Image URL, width, and height variants | `[ {"url": "https://i.ytimg.com/vi/...", "width": 360, "height": 202} ]` |
| `subscriberCountText` | Subscriber count on channel results | `"120K subscribers"` |
| `videoCountText` | Video count on channel results | `"240 videos"` |
| `playlistVideoCount` | Count shown for a playlist or course | `158` |
| `isLive` | Whether a video has a visible live badge | `false` |

The text and numeric count fields serve different purposes. Keep `viewCountText` when you need to reproduce what the search result displayed. Use `viewCount` for sorting or aggregation. A compact value such as `3.5M views` becomes an approximate integer; it does not imply that the source supplied an exact count. A missing count remains `null` rather than being treated as zero.

The search response usually gives relative publication time, such as `7y ago`, rather than a verified calendar date. This actor therefore preserves `publishedTimeText` and does not invent `publishedAt` or `publishDate`. If an exact upload timestamp matters, inspect the linked video page or combine the result with a separate video detail source.

### How to scrape YouTube

1. Enter a keyword or phrase in `query`, such as `python tutorial`.
2. Choose `type`: videos, Shorts, channels, playlists, or movies. Video is the default.
3. Set `maxResults` and optional filters. Start with a moderate cap so you can inspect the output.
4. Run the scraper and open its dataset to review the rows and source links.
5. Download the dataset as JSON, CSV, or Excel, or read it through the API.

A search can contain results beyond the requested cap. The cap is the number of unique rows saved, not the number of search cards inspected. Sponsored cards and unrelated layout elements are excluded. For repeat research, save the exact input alongside the exported dataset so later runs use the same filters and country settings.

### Input

Only `query` is required. The defaults produce video results in English with a United States search setting. Every optional filter narrows or reorders the search at the source, so the result set may change when several are combined.

| Name | Type | Default | Description |
|---|---|---|---|
| `query` | string | none | Required keywords or phrase. |
| `type` | string | `video` | `video`, `shorts`, `channel`, `playlist`, or `movie`. |
| `maxResults` | integer | `100` | Maximum unique rows saved; minimum is one. |
| `scrapeAllResults` | boolean | `false` | Continue through available pages, subject to the 200-page safety limit and the run budget. |
| `sortBy` | string | `relevance` | `relevance` or `popularity`; Shorts use their own order. |
| `duration` | string | unset | Video length: `under3min`, `3to20min`, or `over20min`. |
| `uploadDate` | string | unset | `today`, `week`, `month`, or `year`. |
| `geo` | string | `US` | Two-letter country code for localized search. |
| `lang` | string | `en` | Two-letter interface language code. |
| `features` | object | all off | Boolean video feature filters listed below. |
| `proxyConfiguration` | object | platform proxy | Proxy settings for the public search requests. |

Feature names are `HD`, `subtitles`, `CCommons`, `3D`, `Live`, `Purchased`, `4K`, `360`, `Location`, `HDR`, and `VR180`. Set a feature to `true` to apply it. Enabled features are combined, so a narrow combination can legitimately return few or no results. Leave `features` out to use an unrestricted feature search. Duration and video feature filters are most useful with `type: "video"`.

A realistic, bounded input:

```json
{
  "query": "python tutorial",
  "type": "video",
  "maxResults": 40,
  "geo": "US",
  "lang": "en"
}
```

For a filtered research run, add fields such as `"uploadDate": "month"`, `"sortBy": "popularity"`, and `"features": {"HD": true}`. Check the first page before increasing `maxResults`, especially for a recent-date search. Use a separate run for each query if you want to compare topics; `query` is a single search phrase, not a list.

### Output example

The following values came from one row in a successful 40-result run for the input above. The full dataset row also contains the remaining keys shown in the field table. The thumbnail URL is shortened here; exports keep the complete URL.

```json
{
  "type": "video",
  "query": "python tutorial",
  "position": 1,
  "videoId": "_uQrJ0TkZlc",
  "url": "https://www.youtube.com/watch?v=_uQrJ0TkZlc",
  "title": "Python Full Course for Beginners",
  "viewCountText": "49,152,339 views",
  "viewCount": 49152339,
  "publishedTimeText": "7y ago",
  "lengthText": "6:14:07",
  "durationSeconds": 22447,
  "thumbnail": [
    {
      "url": "https://i.ytimg.com/vi/_uQrJ0TkZlc/hq720.jpg?...",
      "width": 360,
      "height": 202
    }
  ],
  "isLive": false
}
```

The row is a snapshot of a search result, not a permanent statement about its views or ranking. Both can change between runs. `position` refers to the saved results after duplicate IDs are removed; it is useful for comparing exports from the same query and filters, but a later search may place the same item elsewhere.

### Use cases

- **Content planning:** collect public titles, descriptions, and view counts for a topic, then identify recurring formats and questions that appear in search.
- **Creator discovery:** search for channels in a niche, keep channel IDs and URLs, and build a review list for potential partnerships or outreach.
- **Trend monitoring:** schedule the same query and upload-date filter, then compare video IDs across runs to spot newly visible results.
- **Short-form research:** search Shorts separately to study how short clips are titled and how their public view counts change.
- **Playlist discovery:** find public playlists and courses around a subject, including their displayed item count and source links.
- **Reporting:** export a bounded search snapshot with stable IDs, source URLs, and normalized numeric fields for a dashboard or spreadsheet.

These workflows start from a search snapshot. Search results are personalized and ranked by the source, so the dataset should not be interpreted as a complete inventory of every matching item. The country and language controls help make repeated snapshots more consistent, but they do not freeze ranking.

### How much does it cost to scrape YouTube?

The configured result event price is **$1.00 per 1,000 saved results**. That is $0.001 for each row written to the dataset. For example, 40 saved results cost **$0.04**, 100 cost **$0.10**, and 1,000 cost **$1.00** in result charges. The result count is the amount actually saved; asking for 1,000 does not charge for rows the search never returns.

A single run's cap controls your upper bound. If you set `maxResults` to 100, the result charge is at most $0.10 for that run. If you enable `scrapeAllResults`, the row count can be much larger, so use it only after a bounded trial. Check the current Pricing tab before a large run; the displayed price and your account's available credit determine the final charge. Search breadth, filtering, and source availability affect how many rows can be delivered.

### Tips for best results

Start with one clear search phrase. Very broad keywords can return many pages with overlapping videos, while an overly specific phrase may have fewer results than your cap. Use `uploadDate` to focus on recent material, and use `sortBy: "popularity"` when comparing widely viewed results rather than the default relevance order. A date filter and popularity sorting can be combined.

Run different result types separately. A channel result has subscriber and video-count text but no video length. A playlist result can show a count of videos or lessons, while an individual video can have duration and views. Separate runs produce easier exports and prevent you from treating a missing field as a parsing error. If you need both videos and channels, save the two datasets with the same query and compare them using `query`, `channelId`, and source URLs.

Set `geo` and `lang` deliberately for international research. The default is `US` and `en`. A country code changes the search context; an interface language can change text labels and sometimes result ordering. Numeric parsing is best with English count labels, so inspect `viewCountText` when using another language. Keep both settings in your recurring input if you compare runs over time.

Use feature filters sparingly. Selecting `HD` and `subtitles` asks the source for items matching both. Add `4K`, `HDR`, or `VR180` only when those properties are central to the task. The actor sends the selected filters together; it does not infer a feature from the title or thumbnail after the fact. For hard-to-find subjects, remove one filter at a time if the result count is low.

### Limits

The public search response determines the available rows. `maxResults` is a ceiling rather than a promise. A search can end early, return fewer cards for a narrow query, or present overlapping items across pages. The scraper deduplicates IDs and follows continuation tokens until the source ends, the requested count is reached, a token repeats, or the run budget stops output. `scrapeAllResults` has a 200-page safety limit.

Data coverage varies by type. Video results generally provide IDs, titles, URLs, thumbnails, channel names, view counts, and a displayed length. Shorts shelf cards may omit the channel, description, and publication time. Channel results can have subscriber and video-count text but no individual video statistics. Playlist results can contain a displayed item count, though not every playlist has that badge. Movie searches may be small for a particular phrase. The scraper does not fabricate missing values.

Search displays relative publication time. Exact upload timestamps, transcripts, engagement metrics, comments, and full video descriptions are outside this search dataset. View counts can be rounded by the source and can change between snapshots. Thumbnails are links to public images, not downloaded files. Results can vary by country, language, source updates, and the available proxy location. A page or endpoint change can reduce coverage until the parser is updated.

### Use with connected workflows

The actor's input and dataset can be used through connected research workflows. Keep requests bounded, include a query and result type, and ask for source URLs in any summary so the underlying results can be checked.

- “Find 40 public videos for `python tutorial` in the United States. List the title, video URL, displayed views, length, and publication text.”
- “Find public channels for `home gardening` and return channel IDs, URLs, subscriber text, and a short comparison of the search results.”

For repeated analysis, request the same filters each time. Match records by `videoId`, `channelId`, or `playlistId` when comparing exports. Treat a missing optional value as unavailable source data instead of guessing it from another field.

### FAQ

**Is it legal to collect these results?** The scraper reads public search metadata. Follow the source site's terms, applicable law, privacy rules, and rights in the material. Avoid misuse of personal data and verify permissions before republishing content.

**Do I need a YouTube data key?** No user-supplied key is needed for the public search workflow. Enter a query and run the actor. The linked source pages remain the place to verify a result.

**Do I need to configure a proxy?** The default configuration uses the platform proxy. You can change `proxyConfiguration` if your account and use case require another setup.

**How fast is a run?** Time depends on result count, filter breadth, continuation pages, and source response speed. The 40-result verification run completed in under a minute, but that is an observed example rather than a speed guarantee. Start with a bounded run to estimate your own workload.

**Can I schedule and monitor runs?** Yes. Save an input and schedule recurring runs. Compare each dataset's IDs and source links to find newly appearing or disappearing search results. Monitor row count as well as run status so a completed run with unusually few rows gets attention.

**Can I export to a spreadsheet?** Yes. Download CSV or Excel from the dataset, or move rows through an integration. Keep IDs, URLs, query, and position columns so later snapshots can be joined and deduplicated.

**Why is a field empty?** The source response did not include it for that result type or item. For example, a Shorts shelf card may show a title and views without a channel name. `null` means unavailable, not zero. Check the public URL if the field is important to a decision.

**Why did I get fewer results than requested?** The source may offer fewer matching results, a filter combination may be narrow, or pages may repeat IDs. Widen the phrase or remove filters and try a small run before raising the cap.

### Integrations

Start runs through the API, read the default dataset after completion, or connect a webhook to a downstream workflow. Scheduling supports recurring search snapshots. Automation services can move rows into a spreadsheet or reporting system; CSV, JSON, and Excel downloads support manual workflows. Preserve `query`, `type`, IDs, and source URLs when combining datasets so each row remains traceable to the search that produced it.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `query` (type: `string`):

Keywords to search on YouTube.

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

Choose videos, Shorts, channels, playlists, or movies.

## `maxResults` (type: `integer`):

Maximum unique results saved when scrapeAllResults is off.

## `scrapeAllResults` (type: `boolean`):

Follow available continuation pages until the source ends, the run budget is reached, or the 200-page safety limit is reached.

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

Order by relevance or popularity. Shorts search uses its own ordering.

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

Optional duration filter for video results.

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

Optional recent upload period.

## `geo` (type: `string`):

Two-letter country code for localized search, such as US or GB.

## `lang` (type: `string`):

Two-letter interface language code, such as en or es.

## `features` (type: `object`):

Optional video feature filters; selected features are combined.

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

Proxy settings used for the public search requests.

## Actor input object example

```json
{
  "query": "python tutorial",
  "type": "video",
  "maxResults": 100,
  "scrapeAllResults": false,
  "sortBy": "relevance",
  "geo": "US",
  "lang": "en",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

Public search results 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 = {
    "query": "python tutorial"
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/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 = { "query": "python tutorial" }

# Run the Actor and wait for it to finish
run = client.actor("mlg14/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 '{
  "query": "python tutorial"
}' |
apify call mlg14/youtube-search-scraper --silent --output-dataset

```

## MCP server setup

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