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

Scrape YouTube videos from search results, channels (videos, Shorts, streams) or video URLs with full details: exact views, likes, comment count, publish date, subscribers, description and hashtags. No API key.

- **URL**: https://apify.com/solarg/youtube-scraper.md
- **Developed by:** [Solar G](https://apify.com/solarg) (community)
- **Categories:** Videos, Social media
- **Security:** Limited permissions
- **Stats:** 2 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.35 / 1,000 videos

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

Scrapes YouTube videos from search results, channels and individual video URLs, with details for every video: exact views and likes, comment count, publish date, duration, channel and subscribers, collaborators, description and hashtags. Tags, category and the exact publish time are included when YouTube serves its player data, which it often withholds from cloud servers (see the field notes). No YouTube API key or quota is needed.

For a quick one-off check without an Apify account, a free web version with a small daily limit is available at [purfled.com/youtube-video-stats](https://purfled.com/youtube-video-stats/).

### Input

Use any combination of the three sources:

| Field | Description |
|---|---|
| `searchQueries` | Keywords to search. |
| `channels` | `@handle`, channel URL or channel ID. A tab in the URL (`/@name/shorts`, `/@name/streams`) is respected. |
| `videoUrls` | Video, Shorts or live URLs, or 11-character video IDs. Several URLs on one line separated by spaces are split. |
| `maxResults` | Limit per search query and per channel tab. Default 50. Video URLs are always processed in full. |
| `includeDetails` | Full details for every video. Default `true`. Turn it off for a faster, cheaper listing. |
| `channelTabs` | `videos`, `shorts`, `streams`. Default `["videos"]`. |
| `sortBy` | Search order: `relevance` or `views`. |
| `uploadDate` | Search filter: `any`, `hour`, `today`, `week`, `month`, `year`. |
| `duration` | Search filter: `any`, `short` (under 4 min), `medium` (4-20 min), `long` (over 20 min). |
| `country` / `language` | Region and language for search and channel listings. Default `US` / `en`. Video details are always read in English, so numbers and dates parse the same way in every language. |

```json
{
  "searchQueries": ["home espresso machine review"],
  "channels": ["@veritasium"],
  "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "maxResults": 100,
  "uploadDate": "month"
}
```

### Output

Every record has the same keys; a value is `null` when YouTube does not show it. Example from a real run (description shortened):

```json
{
  "videoId": "dQw4w9WgXcQ",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)",
  "channel": "Rick Astley",
  "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw",
  "channelUrl": "https://www.youtube.com/@RickAstleyYT",
  "channelHandle": "@RickAstleyYT",
  "channelSubscribers": 4550000,
  "channelVerified": true,
  "collaborators": [],
  "views": 1829827344,
  "concurrentViewers": null,
  "likes": 19493596,
  "commentCount": 2460393,
  "commentCountIsExact": true,
  "duration": "3:33",
  "durationSec": 213,
  "publishDate": "2009-10-24",
  "publishedAt": "2009-10-25T06:57:33.000Z",
  "isShort": false,
  "isLive": false,
  "isAgeRestricted": false,
  "isMembersOnly": false,
  "commentsTurnedOff": false,
  "tags": ["rick astley", "Never Gonna Give You Up", "nggyu", "..."],
  "category": "Music",
  "thumbnail": "https://i.ytimg.com/vi/dQw4w9WgXcQ/maxresdefault.jpg",
  "description": "The official video for “Never Gonna Give You Up” by Rick Astley. ...",
  "hashtags": ["#RickAstley", "#NeverGonnaGiveYouUp", "#OfficialMusicVideo"],
  "source": "url",
  "sourceValue": "dQw4w9WgXcQ",
  "input": "dQw4w9WgXcQ",
  "position": null,
  "publishedTimeText": null,
  "publishedAtApprox": null,
  "dateText": "Oct 24, 2009"
}
```

Field notes:

- `views` and `likes` are exact. `likes` is `null` when the creator hides it. `channelSubscribers` is rounded by YouTube (for example 4.55M).
- `commentCount` is exact when `commentCountIsExact` is `true`. If the exact count cannot be read, the rounded figure YouTube shows (for example 5.7K) is returned with `commentCountIsExact: false`.
- Videos published by several channels have every channel in `collaborators`; the `channel` fields hold the first one.
- `publishDate` is the date YouTube displays, which is in US Pacific time. `publishedAt` is the exact timestamp in UTC, so it can fall on the next day (as in the example above).
- Live streams have `isLive: true` and `concurrentViewers` instead of `views`.
- Age-restricted videos have `isAgeRestricted: true`. YouTube does not show their comments to logged-out visitors, so `commentCount` is `null`.
- YouTube often withholds player data from data-centre IP addresses, including Apify's servers, so on many runs most records lack it. On those records `tags`, `category`, `publishedAt` and `isMembersOnly` are `null` (unknown), and `duration` is taken from the search or channel listing or estimated from the replay heatmap (it can then differ by one second). `durationSec` is `null` for a video requested by URL that has no heatmap yet, such as a new Short. `tags: []` means the video has no tags; `tags: null` means they could not be read.
- `source` / `sourceValue` say where a video was found (`search`, `channel` or `url`). `position` is the rank in search results.
- A video found by more than one source is returned once.

Errors:

- An input that is not a YouTube URL, ID, channel or valid search gives an item with `input` and `error` (for example "Not a YouTube video URL or ID"). Video URLs that are private or removed give "Video not found, private or removed". Error items are not charged.
- A search or channel tab with no videos gives an error item explaining that.
- If details fail for a video found by search or on a channel, the listing data is kept and `detailsError` explains why. That record is charged as a listing only.

### API usage

```bash
curl -X POST "https://api.apify.com/v2/acts/solarg~youtube-scraper/run-sync-get-dataset-items?token=YOUR_API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"searchQueries": ["home espresso machine review"], "maxResults": 50}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_API_TOKEN")
run = client.actor("solarg/youtube-scraper").call(run_input={"channels": ["@veritasium"], "maxResults": 100})
items = client.dataset(run["defaultDatasetId"]).list_items().items
```

### Pricing

- $0.50 per 1,000 videos (listing)
- $0.50 per 1,000 videos for full details (charged only when `includeDetails` is on and the details were retrieved)

That is $1.00 per 1,000 fully detailed videos. Error items are free. Apify paid plans get lower prices: 10% off on Bronze, 20% on Silver and 30% on Gold and above. Platform usage is included in the price.

### Limitations

- Only public data that YouTube shows to logged-out visitors is collected. Private, members-only and removed videos cannot be read.
- Dislike counts are not public and are not returned.

Report problems in the Issues tab.

# Actor input Schema

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

Keywords to search on YouTube.

## `channels` (type: `array`):

@handles, channel URLs or channel IDs. A tab in the URL (/@name/shorts, /@name/streams) is used instead of Channel content for that channel.

## `videoUrls` (type: `array`):

Individual videos, Shorts or live streams (URLs or 11-character IDs). Several URLs on one line separated by spaces are split.

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

Limit for each search query and each channel tab. Video URLs are always processed in full.

## `includeDetails` (type: `boolean`):

Exact views, likes, comment count, publish date, subscribers, description and hashtags for every video. Turn off for a faster, cheaper listing.

## `channelTabs` (type: `array`):

Which channel tabs to collect for each channel: regular videos, Shorts, and live or past streams.

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

Order of search results: YouTube relevance or highest view count first. Applies to search queries only.

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

Only videos uploaded within this period.

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

Filter search results by video length: under 4 minutes, 4 to 20 minutes, or over 20 minutes.

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

Two-letter country code for search and channel listings, e.g. US, GB, DE.

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

Language code for search and channel listings, e.g. en, de, es. Video details are always read in English so numbers and dates parse correctly.

## Actor input object example

```json
{
  "searchQueries": [
    "home espresso machine review"
  ],
  "maxResults": 20,
  "includeDetails": true,
  "channelTabs": [
    "videos"
  ],
  "sortBy": "relevance",
  "uploadDate": "any",
  "duration": "any",
  "country": "US",
  "language": "en"
}
```

# Actor output Schema

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

Videos in table form (the Videos view of the dataset).

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

Every item in the default dataset, including error items.

# 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"
    ],
    "maxResults": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("solarg/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": ["home espresso machine review"],
    "maxResults": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("solarg/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": [
    "home espresso machine review"
  ],
  "maxResults": 20
}' |
apify call solarg/youtube-scraper --silent --output-dataset

```

## MCP server setup

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