# YouTube Scraper (`solidcode/youtube-scraper`) Actor

Extract YouTube videos, shorts, channels, and playlists from search queries, URLs, or channel handles. Returns titles, view counts, publish dates, channel info, thumbnails, durations across 42 countries and 19 languages. Video and Shorts URLs also return exact like counts.

- **URL**: https://apify.com/solidcode/youtube-scraper.md
- **Developed by:** [SolidCode](https://apify.com/solidcode) (community)
- **Categories:** Videos, Social media, Developer tools
- **Stats:** 128 total users, 23 monthly users, 100.0% runs succeeded, 2 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

from $0.50 / 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?

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 Scraper

Extract YouTube videos, shorts, channels, and playlists at scale. Feed the actor search queries, YouTube URLs (video, channel, playlist, shorts, or search pages), or channel handles like `@mkbhd` — mix and match in a single run. Each result comes back as a clean, structured row with titles, view counts, publish dates, channel info, thumbnails, and durations. Built for media analysts, agencies, and researchers who need reliable YouTube data in a spreadsheet the same afternoon they think of the question.

### Why This Scraper?

- **One input surface, three input types** — search queries, URLs, and `@handles` can all run in the same job, and every row is tagged with `recordType` (`video`, `short`, `channel`, or `playlist`) so you always know what you're looking at.
- **Nine video-feature filters and four sort orders** — combine HD, 4K, HDR, Subtitles, Creative Commons, Live, 360°, VR180, and 3D, then narrow by upload date and duration and sort by relevance, upload date, view count, or rating.
- **Exact like counts, not rounded badges** — pass a video or Shorts URL and you get the precise figure (`19358151`), not the "19M" YouTube prints on the page, in all 19 interface languages.
- **42 countries, 19 interface languages** — pick both from friendly dropdowns (no raw locale codes), and `publishedAt` still resolves to a clean ISO timestamp in every one of them, so a German or Japanese run sorts by date exactly like an English one.
- **Shorts on your terms** — one dropdown exports a channel's Shorts only, its regular videos only, or both, and `youtube.com/@handle/shorts`, `/videos`, and `/streams` links are honoured exactly as written. Keyword searches take the same switch, so you never pay for Shorts you didn't ask for.
- **Sorted across the whole dataset, not page by page** — "Upload date" puts the newest video in row 1 and the oldest in the last row of the entire export, and "View count" does the same for the most-viewed.
- **Channel enrichment** — add `fetchChannelInfo: true` and every channel input emits a row with subscribers, total views, video count, description, country, and creation date.
- **A cap that is really a cap** — `maxResults` is a hard ceiling per source, and Shorts, the channel-details row, and a playlist's summary row all count inside it. Ask for 30 and you are billed for 30 rows, never 31.

### Use Cases

**Market Research**

- Track share of voice on a topic or keyword over time
- Measure how a new product, album, or announcement is performing across creators
- Benchmark total view counts and publish cadence across a category

**Lead Generation**

- Build creator lists in any niche by searching keywords and collecting channel handles
- Enrich channel rosters with subscriber counts, contact info hints (channel description), and country targeting
- Find rising channels before they're discovered by competitors

**Content Strategy**

- See which titles, thumbnails, and durations are winning in your niche
- Identify trending formats (shorts vs. long-form) per category
- Study upload cadence and posting times of top performers

**Competitive Analysis**

- Monitor competitor channels — their full Videos tab plus Shorts
- Track competitor view counts and publish cadence week-over-week
- Spot when a competitor pivots topics or picks up a new trend

**Academic Research**

- Gather large, reproducible video datasets for media studies
- Study engagement patterns across countries and languages
- Sample trending content for cultural and political research

**Trend Monitoring**

- Run the same keyword across several countries to compare what each region is watching
- Detect breakout topics by sorting a topic search by view count within the last 24 hours
- Track recurring trends across time windows using the upload date filter

### Getting Started

#### Simple Search

The fastest way to start — one search query and a result cap.

```json
{
    "searchQueries": ["mkbhd"],
    "maxResults": 20
}
```

#### Filtered Search

Narrow results with upload date, duration, sort order, features, and localization.

```json
{
    "searchQueries": ["electric vehicle review"],
    "uploadDate": "month",
    "duration": "medium",
    "sortBy": "viewCount",
    "features": ["hd", "subtitles"],
    "country": "us",
    "language": "en",
    "maxResults": 100
}
```

#### URL-Based Input

Paste any mix of YouTube URLs — video, channel, playlist, shorts, or search URLs. Toggle `fetchChannelInfo` to also get full channel stats for every channel URL.

```json
{
    "startUrls": [
        "https://www.youtube.com/watch?v=c347oYQO57A",
        "https://www.youtube.com/@mkbhd",
        "https://www.youtube.com/playlist?list=PLpjK416fmKwQ4Jb2vE2lK7K3vzoD9E5nQ"
    ],
    "fetchChannelInfo": true,
    "maxResults": 25
}
```

#### Channel Handles with Shorts

Scrape multiple channels by handle and also collect their Shorts tab. Swap `channelContent` to `"shorts"` for a Shorts-only export, or `"videos"` to keep Shorts out entirely.

```json
{
    "youtubeHandles": ["@mkbhd", "@veritasium", "@LinusTechTips"],
    "includeShorts": true,
    "channelContent": "default",
    "fetchChannelInfo": true,
    "maxResults": 30
}
```

### Input Reference

#### What to Scrape

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `searchQueries` | string\[] | `[]` | Keywords to search on YouTube. Each query runs independently. |
| `startUrls` | string\[] | `[]` | YouTube URLs — accepts video, channel (`/@handle`, `/channel/UC...`, `/c/...`, `/user/...`), playlist, shorts, and search result URLs in the same list. A channel link can name a tab and that tab is what you get: `/@handle/shorts` returns only Shorts, `/@handle/videos` only regular videos, `/@handle/streams` only past live streams. |
| `youtubeHandles` | string\[] | `[]` | Channel handles like `@mkbhd` or `mkbhd` (the leading `@` is optional). Each handle resolves to that channel's Videos tab, or to its Shorts alone when `channelContent` is set to `"shorts"`. |

#### Results

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `maxResults` | integer | `50` | Maximum rows per query, URL, or handle (0–500). A hard ceiling on what that source bills: Shorts, the channel-details row, and a playlist's summary row all count inside it. Set to `0` for as many as YouTube will serve. |
| `includeShorts` | boolean | `false` | Include YouTube Shorts. Off, a keyword search returns long-form videos only. On, a search also returns the Shorts YouTube shows for it, and a channel URL or handle also pulls the channel's Shorts tab. Shorts count toward the same `maxResults` cap, shared evenly between the two tabs; whichever tab runs out first passes its unused slots to the other. |
| `channelContent` | string | `"default"` | Which part of a channel to export, for handles and channel URLs. `default` = videos, plus Shorts when `includeShorts` is on. `videos` = regular videos only, even with `includeShorts` on. `shorts` = that channel's Shorts and nothing else, so you are never billed for videos you didn't ask for (a channel that posts no Shorts returns no rows rather than its videos). A link that already names a tab wins for that one link. |

#### Filters (Search Only)

These apply to keyword searches. URL, handle, and playlist inputs ignore them.

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `uploadDate` | string | `"any"` | Upload recency: Any time / Last hour / Today / This week / This month / This year. |
| `duration` | string | `"any"` | Video length: Any length / Short (under 4 min) / Medium (4–20 min) / Long (over 20 min). |
| `sortBy` | string | `"relevance"` | Sort order: Relevance / Upload date / View count / Rating. Upload date returns the newest video first and View count the most-viewed first, ordered top to bottom across the whole dataset; Relevance and Rating come back in YouTube's own ranking. |
| `features` | string\[] | `[]` | Multi-select video features: HD, Subtitles / CC, Creative Commons, Live, 4K, 360°, VR180, 3D, HDR. Combine as needed. |

#### Localization

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `country` | string | `"us"` | Country for result localization — it shifts which videos YouTube surfaces for a search. Supports United States, United Kingdom, Canada, Australia, New Zealand, Ireland, France, Germany, Spain, Italy, Netherlands, Belgium, Portugal, Switzerland, Austria, Sweden, Norway, Denmark, Finland, Poland, Japan, South Korea, Hong Kong, Taiwan, Singapore, Malaysia, Thailand, Indonesia, Philippines, Vietnam, India, Brazil, Mexico, Argentina, Chile, Colombia, United Arab Emirates, Saudi Arabia, Turkey, Israel, South Africa, and Egypt. |
| `language` | string | `"en"` | Interface language for titles and metadata. English, French, German, Spanish, Italian, Portuguese, Dutch, Polish, Turkish, Russian, Japanese, Korean, Chinese (Simplified), Chinese (Traditional), Arabic, Hebrew, Thai, Vietnamese, Indonesian. |

#### Enrichment

| Parameter | Type | Default | Description |
|-----------|------|---------|-------------|
| `fetchChannelInfo` | boolean | `false` | For channel URLs and handles, emit one row per channel with subscriber count, description, country, and creation date. Counts toward `maxResults`, so a cap of 30 returns 29 videos plus the channel row. |

### Output

Every row carries a `recordType` field so you can tell videos, shorts, channels, and playlists apart at a glance. Below is one example per type.

#### Video

```json
{
    "recordType": "video",
    "id": "qzGxK6Uiu04",
    "url": "https://www.youtube.com/watch?v=qzGxK6Uiu04",
    "title": "Incredible Wildlife in Stunning 16K HDR 120fps Dolby Vision",
    "description": "Experience the incredible wildlife in stunning 16K HDR 120fps Dolby Vision...",
    "duration": 4952,
    "durationText": "1:22:32",
    "viewCount": 19552604,
    "likeCount": null,
    "publishedText": "1 year ago",
    "publishedAt": "2025-04-24T17:46:41+00:00",
    "isLive": false,
    "isShort": false,
    "thumbnails": [
        {"url": "https://i.ytimg.com/vi/qzGxK6Uiu04/hq720.jpg", "width": 720, "height": 404}
    ],
    "channel": {
        "id": "UChB3UnDddahXU7FKZXmpzMA",
        "name": "8K Earth",
        "handle": "@8kEarth",
        "url": "https://www.youtube.com/@8kEarth"
    },
    "sourceInput": "8k hdr nature",
    "scrapedAt": "2026-04-24T17:46:41+00:00"
}
```

*This row came from a search query, so `likeCount` is `null` — YouTube doesn't publish like counts on search results. Pass the video's own URL in `startUrls` and the exact count comes back, as in the Short example below.*

#### Short

*Illustrative example — individual YouTube video IDs can be deleted or set to private by their creator at any time, so don't rely on any specific ID in this document for live data.*

```json
{
    "recordType": "short",
    "id": "s3DrGkDvgNw",
    "url": "https://www.youtube.com/shorts/s3DrGkDvgNw",
    "title": "You won't believe this camera trick",
    "description": null,
    "duration": 49,
    "durationText": "0:49",
    "viewCount": 1824300,
    "likeCount": 96420,
    "publishedText": null,
    "publishedAt": "2026-03-11T15:22:04+00:00",
    "isLive": false,
    "isShort": true,
    "thumbnails": [
        {"url": "https://i.ytimg.com/vi/s3DrGkDvgNw/hq720.jpg", "width": 720, "height": 1280}
    ],
    "channel": {
        "id": "UCBJycsmduvYEL83R_U4JriQ",
        "name": "Marques Brownlee",
        "handle": "@mkbhd",
        "url": "https://www.youtube.com/@mkbhd"
    },
    "sourceInput": "https://www.youtube.com/shorts/s3DrGkDvgNw",
    "scrapedAt": "2026-04-24T17:46:41+00:00"
}
```

#### Channel

Emitted only when `fetchChannelInfo: true` and the input is a channel URL or handle.

```json
{
    "recordType": "channel",
    "id": "UCBJycsmduvYEL83R_U4JriQ",
    "url": "https://www.youtube.com/@mkbhd",
    "title": "Marques Brownlee",
    "handle": "@mkbhd",
    "subscriberCount": 20900000,
    "subscriberCountText": "20.9M subscribers",
    "videoCount": 1816,
    "viewCount": 5361395937,
    "description": "MKBHD: Quality Tech Videos | YouTuber | Geek | Consumer Electronics...",
    "country": "United States",
    "joinedAt": "2008-03-21",
    "joinedText": "Joined 21 Mar 2008",
    "thumbnail": "https://yt3.googleusercontent.com/ytc/mkbhd_avatar.jpg",
    "banner": "https://yt3.googleusercontent.com/ytc/mkbhd_banner.jpg",
    "sourceInput": "@mkbhd",
    "scrapedAt": "2026-04-24T17:46:41+00:00"
}
```

#### Playlist

Emitted once per playlist URL, alongside the videos it contains. It counts toward `maxResults` like any other row, so a playlist capped at 30 returns this row plus 29 videos.

```json
{
    "recordType": "playlist",
    "id": "PLpjK416fmKwQ4Jb2vE2lK7K3vzoD9E5nQ",
    "url": "https://www.youtube.com/playlist?list=PLpjK416fmKwQ4Jb2vE2lK7K3vzoD9E5nQ",
    "title": "Retro Tech",
    "description": "Revisiting the best tech of decades past.",
    "videoCount": 42,
    "channel": {
        "id": "UCBJycsmduvYEL83R_U4JriQ",
        "name": "Marques Brownlee",
        "handle": "@mkbhd",
        "url": "https://www.youtube.com/@mkbhd"
    },
    "thumbnails": [
        {"url": "https://i.ytimg.com/vi/retrothumb/hqdefault.jpg", "width": 480, "height": 360}
    ],
    "sourceInput": "https://www.youtube.com/playlist?list=PLpjK416fmKwQ4Jb2vE2lK7K3vzoD9E5nQ",
    "scrapedAt": "2026-04-24T17:46:41+00:00"
}
```

#### Fields by Record Type

**Common fields (every row)**

| Field | Type | Description |
|-------|------|-------------|
| `recordType` | string | One of `video`, `short`, `channel`, `playlist`. |
| `id` | string | Canonical YouTube ID (videoId, channelId, or playlistId). |
| `url` | string | Public YouTube URL. |
| `title` | string | Video title, channel name, or playlist name. |
| `sourceInput` | string | The original query, URL, or handle that produced this row. |
| `scrapedAt` | string | ISO 8601 timestamp of when the row was captured. |

**Video & Short fields**

| Field | Type | Description |
|-------|------|-------------|
| `description` | string | null | Video description (snippet on search results, full text on direct video URLs). |
| `duration` | integer | null | Length in seconds. `null` for live streams. |
| `durationText` | string | null | Human-readable duration (e.g. `"12:34"`). |
| `viewCount` | integer | null | Total views. |
| `likeCount` | integer | null | Exact like count. Populated on direct video and Shorts URLs; `null` on search, channel, and playlist rows (YouTube omits likes from those listings) and on the occasional video YouTube serves without its like button. |
| `publishedText` | string | null | Human-readable publish label (e.g. `"2 weeks ago"`, `"vor 3 Monaten"`). |
| `publishedAt` | string | null | Resolved ISO-8601 UTC timestamp, in all 19 interface languages. Always UTC (`+00:00`). |
| `isLive` | boolean | `true` if the video is currently live. |
| `isShort` | boolean | `true` for YouTube Shorts. |
| `thumbnails` | object\[] | Thumbnail renditions with `url`, `width`, `height`. |
| `channel.id` | string | Parent channel ID. |
| `channel.name` | string | Parent channel name. |
| `channel.handle` | string | null | Channel handle (`@handle`). |
| `channel.url` | string | Channel URL. |

**Channel fields** (`recordType: "channel"`)

| Field | Type | Description |
|-------|------|-------------|
| `handle` | string | null | Channel handle in `@handle` form. |
| `subscriberCount` | integer | null | Numeric subscriber count. |
| `subscriberCountText` | string | null | Original label (e.g. `"20.9M subscribers"`). |
| `videoCount` | integer | null | Total videos uploaded. |
| `viewCount` | integer | null | All-time channel views. |
| `description` | string | null | Channel description. |
| `country` | string | null | Listed channel country. |
| `joinedAt` | string | null | Channel creation date (ISO format). |
| `joinedText` | string | null | Original "Joined" label from the About tab. |
| `thumbnail` | string | null | Channel avatar URL. |
| `banner` | string | null | Channel banner URL. |

**Playlist fields** (`recordType: "playlist"`)

| Field | Type | Description |
|-------|------|-------------|
| `description` | string | null | Playlist description if present. |
| `videoCount` | integer | null | Number of videos in the playlist. |
| `channel` | object | `{id, name, handle, url}` of the playlist owner. |
| `thumbnails` | object\[] | Playlist thumbnail renditions. |

### Tips for Best Results

- **Use exact phrases in quotes** (`"\"galaxy s25 review\""`) for narrower search results.
- **`maxResults: 0` pulls everything YouTube will serve** — typically 500–700 rows per search query and up to several thousand per channel. Set a concrete cap when you need a predictable run.
- **Pass a video's own URL when you want the full record** — a `/watch?v=<id>` or `/shorts/<id>` link returns the exact like count, the full description, and a Short's view count and publish date. Rows gathered from searches, channel tabs, and playlists come back with `likeCount: null`, and Shorts taken from a channel's Shorts tab carry no duration or publish date, because YouTube doesn't publish those on its listing pages. URL input is forgiving: `youtu.be/abc123` short links, long `watch?v=` URLs with extra parameters, and mobile URLs all work.
- **Country and language matter** — they shift which videos YouTube surfaces for the same words. Upload dates land in `publishedAt` as clean ISO timestamps in every one of the 19 languages, so a German or Japanese run sorts and filters by date exactly like an English one.
- **Mix inputs freely** — one run can combine search queries, direct video URLs, and channel handles. Every row keeps a `sourceInput` field so you can group results by origin in your downstream pipeline.
- **Search filters are keyword-only** — applying `uploadDate` or `duration` to a URL or handle input has no effect. To narrow channel uploads, post-filter on `publishedAt` and `duration` after extraction.
- **Want what is popular right now?** YouTube retired its Trending page in 2025 and no longer publishes one anywhere. Search a topic with `sortBy: "viewCount"` and `uploadDate: "today"` (or `"week"`) instead — that is the closest thing YouTube still serves, and you can run it per country.
- **Know what is out of scope before you plan a pipeline** — comments and replies (use a dedicated YouTube comments actor), age-restricted and members-only videos (YouTube serves those only to signed-in viewers), and playback or caption files are not returned.

### Pricing

**From $0.50 per 1,000 results** — and that is the rate on every Apify discount tier, so a free-plan run costs exactly what a Gold-tier run costs.

| Results | No discount | Bronze | Silver | Gold |
|---------|-------------|--------|--------|------|
| 100 | $0.05 | $0.05 | $0.05 | $0.05 |
| 1,000 | $0.50 | $0.50 | $0.50 | $0.50 |
| 10,000 | $5.00 | $5.00 | $5.00 | $5.00 |
| 100,000 | $50.00 | $50.00 | $50.00 | $50.00 |

Every row in your dataset counts as one result, regardless of record type. No compute or time-based charges — you pay per result, plus a small fixed per-run start fee.

### Integrations

Export your data or plug it into your existing stack:

- **API** — Pull results programmatically via the Apify API
- **Webhooks** — Trigger downstream workflows when a run completes
- **Google Sheets** — One-click export to spreadsheets
- **Zapier / Make / n8n** — Automate with 1,000+ app connectors
- **Datasets** — Download as JSON, CSV, Excel, XML, or RSS

### Legal & Ethical Use

This actor is designed for legitimate market research, content strategy, competitive analysis, and academic research. You are responsible for complying with YouTube's Terms of Service, applicable laws, and data protection regulations in your jurisdiction. The actor only collects publicly visible data — no login, no paywalled or age-gated content, no personal data beyond what YouTube surfaces on public pages. Do not use extracted data for spam, harassment, impersonation, or any unlawful purpose.

# Actor input Schema

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

Keywords to search on YouTube (e.g., 'mkbhd review', 'guitar tutorial'). Each query runs independently. Leave empty if you only want to scrape URLs or handles. To see what is popular right now, search a topic and set Sort By to "View count" with an Upload Date of "Today" or "This week" — YouTube retired its Trending page in 2025 and no longer publishes one.

## `startUrls` (type: `array`):

Paste YouTube URLs directly. Accepts video URLs (youtube.com/watch?v=...), channel URLs (/@handle, /channel/UC..., /c/..., /user/...), playlist URLs (/playlist?list=...), shorts URLs (/shorts/...), and search result URLs (/results?search\_query=...). Mix any of these in one list. A channel link can name a tab and that tab is what you get: /@handle/shorts returns only that channel's Shorts, /@handle/videos only its regular videos, /@handle/streams only its past live streams.

## `youtubeHandles` (type: `array`):

YouTube channel handles like '@mkbhd' or 'mkbhd'. Each handle resolves to that channel's Videos tab, or to its Shorts alone if you set Channel Content to "Shorts only". The leading '@' is optional.

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

Maximum number of rows per query, URL, or handle. This is a hard ceiling on what you are billed for that source — every row counts toward it, including the Shorts a channel scrape returns, the channel-details row when Fetch Channel Details is on, and the playlist summary row a playlist link returns. So a playlist link capped at 30 returns the playlist row plus 29 of its videos, never 31 rows. Set to 0 to fetch every available result (YouTube typically caps search at roughly 500-700 per query).

## `includeShorts` (type: `boolean`):

Include YouTube Shorts in the results. Off (the default) returns long-form videos only, so a keyword search never spends your result budget on Shorts. On, a keyword search also returns the Shorts YouTube shows for that search, and a channel URL or handle also pulls the channel's Shorts tab in addition to the Videos tab. Shorts count toward the same Maximum Results cap as videos: on channel scrapes the cap is shared evenly between the two tabs, and whichever tab runs out first passes its unused slots to the other. Want a channel's Shorts and nothing else? Set Channel Content below to "Shorts only". Shorts found on a keyword search carry no publish date and no channel details — YouTube does not publish those on its Shorts shelf.

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

Which part of a channel to export. Applies to channel handles and channel URLs only. "Shorts only" returns that channel's Shorts and nothing else, so you are never billed for regular videos you did not ask for — and a channel that posts no Shorts returns no rows instead of its videos. "Regular videos only" is the opposite: no Shorts, even with Include Shorts ticked. Leave it on the standard option to keep the usual behaviour. A channel link that already names a tab wins for that one link: youtube.com/@handle/shorts returns Shorts, /videos returns regular videos, /streams returns past live streams. Ignored for keyword searches, playlists, and single video or Shorts links.

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

Only include videos uploaded within this time window. Applies to keyword searches only.

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

Filter search results by video length. Applies to keyword searches only.

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

Ordering for keyword-search results. Applies to keyword searches only. Rows are put in order before they are saved, so "Upload date" gives you the newest video first and "View count" the most-viewed first, all the way down the dataset. Two things to know: YouTube reports search upload dates rounded to the hour or day, so videos published inside the same reported window keep YouTube's order; and rows YouTube publishes no upload date or view count for (Shorts taken from its Shorts shelf) are placed at the end rather than dropped. Because the ordering covers the whole result set, the rows are saved when the search finishes rather than page by page; stop a run before then and everything it had already collected is waiting in that run's key-value store under PARTIAL-RESULTS. "Relevance" and "Rating" are YouTube's own rankings — it serves those results in its own order and publishes no rating value, so they are returned exactly as YouTube sent them.

## `features` (type: `array`):

Only include videos with these features. Multiple features can be combined. Applies to keyword searches only. "Subtitles" = videos with any caption track. "Creative Commons" = videos released under a CC license.

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

Localize results to this country. Affects which videos YouTube surfaces for a search and the regional metadata returned with them.

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

Interface language for video titles and metadata returned in the response.

## `fetchChannelInfo` (type: `boolean`):

When scraping a channel URL or handle, also emit one row per channel with subscriber count, description, country, and creation date. That row counts toward Maximum Results like any other row, so it never adds to your bill: a cap of 30 returns 29 videos plus the channel row, not 31 rows.

## Actor input object example

```json
{
  "searchQueries": [],
  "startUrls": [],
  "youtubeHandles": [],
  "maxResults": 50,
  "includeShorts": false,
  "channelContent": "default",
  "uploadDate": "any",
  "duration": "any",
  "sortBy": "relevance",
  "features": [],
  "country": "us",
  "language": "en",
  "fetchChannelInfo": false
}
```

# Actor output Schema

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

Table of all results with the most useful fields at a glance.

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

Per-video detail rows with duration, view count, channel info, and thumbnails. The Likes column fills in only for rows scraped from a video or Shorts URL — YouTube does not publish like counts on search results or channel listings.

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

Per-channel rows with subscriber count, description, country, and creation date. Only populated when 'Fetch Channel Details' is enabled.

# 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": [],
    "startUrls": [],
    "youtubeHandles": [],
    "maxResults": 50,
    "includeShorts": false,
    "channelContent": "default",
    "uploadDate": "any",
    "duration": "any",
    "sortBy": "relevance",
    "features": [],
    "country": "us",
    "language": "en",
    "fetchChannelInfo": false
};

// Run the Actor and wait for it to finish
const run = await client.actor("solidcode/youtube-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": [],
    "startUrls": [],
    "youtubeHandles": [],
    "maxResults": 50,
    "includeShorts": False,
    "channelContent": "default",
    "uploadDate": "any",
    "duration": "any",
    "sortBy": "relevance",
    "features": [],
    "country": "us",
    "language": "en",
    "fetchChannelInfo": False,
}

# Run the Actor and wait for it to finish
run = client.actor("solidcode/youtube-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": [],
  "startUrls": [],
  "youtubeHandles": [],
  "maxResults": 50,
  "includeShorts": false,
  "channelContent": "default",
  "uploadDate": "any",
  "duration": "any",
  "sortBy": "relevance",
  "features": [],
  "country": "us",
  "language": "en",
  "fetchChannelInfo": false
}' |
apify call solidcode/youtube-scraper --silent --output-dataset

```

## MCP server setup

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