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

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

## Pricing

from $1.25 / 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: Videos, Channels, Search & Comments

Collect YouTube video statistics, channel profiles, search results and comments in one run. Paste video links, channel links or @handles, and search terms in any mix. Each video, channel and comment becomes one clean row with views, likes, comment count, duration, publish date, subscribers, descriptions and hashtags. You pay only for rows that are actually delivered.

Need transcripts? Use the companion [YouTube Transcripts Scraper](https://apify.com/barefoot_grade/youtube-transcripts-scraper) for timestamped captions of the same videos.

### Quick Start

```json
{
  "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "channelUrls": ["https://www.youtube.com/@mkbhd"],
  "searchQueries": ["python tutorial"],
  "maxResults": 10,
  "maxComments": 50
}
```

This run returns the video's full statistics plus its top 50 comments, the MKBHD channel profile plus its 10 latest uploads, and the first 10 videos YouTube shows for "python tutorial".

### Use cases

- **Creator and competitor research**: track subscribers, total views, upload cadence and per-video performance for any set of channels.
- **Content and SEO research**: see which videos rank for a search term, with views, duration and upload age.
- **Audience and sentiment analysis**: export comments and replies with likes and reply counts for any video.
- **Campaign reporting**: refresh views, likes and comment counts for a list of sponsored or owned videos.

### Input

| Input | Type | Default | Meaning |
| --- | --- | --- | --- |
| `videoUrls` | array | — | Watch, youtu.be, Shorts or live links, or bare 11-character video IDs. Each becomes one full video row. |
| `channelUrls` | array | — | Channel links, @handles or `UC…` channel IDs. Each adds one channel profile row plus its latest uploads. |
| `searchQueries` | array | — | Search terms. Videos are returned in YouTube's result order. |
| `maxResults` | integer | `20` | Videos per search term and per channel. `0` on a channel run returns only the profile row. |
| `videoDetails` | boolean | `false` | Also fetch likes, comment count, exact publish date, subscribers and the full description for search and channel videos. Slower. |
| `includeShorts` | boolean | `true` | Keep Shorts that appear in search results. |
| `includeChannelInfo` | boolean | `true` | Add the channel profile row for each channel. |
| `maxComments` | integer | `0` | Comments per video in `videoUrls`. Replies count toward this limit. |
| `commentsSort` | string | `top` | `top` (YouTube's ranking) or `newest`. |
| `includeReplies` | boolean | `false` | Also collect replies, each marked with its parent comment ID. |
| `maxItems` | integer | `1000` | Hard stop for the whole run across every input and row type, so you control the cost. |
| `proxyConfiguration` | object | Apify proxy, US | Optional proxy settings; the default works for most runs. Use a direct connection, Apify datacenter proxy or your own proxy URLs. |

Inputs are processed in order: video links first, then channels, then search terms. A video that appears under several inputs is delivered once.

### What it returns

Every row has a `type` (`video`, `channel` or `comment`), the `input` that produced it and a `scrapedAt` timestamp.

**Video rows**

| Field | Meaning |
| --- | --- |
| `videoId`, `url`, `title`, `description` | Video identity and text. Search and channel rows carry the result-list snippet unless `videoDetails` is on. |
| `viewCount`, `likeCount`, `commentCount` | Engagement. Likes and comment count come with full details (video links, or `videoDetails`). Comment counts above 1,000 are rounded the way YouTube shows them. |
| `publishedAt`, `publishedText` | Publish date (`YYYY-MM-DD`) and the date as YouTube shows it, for example `2 days ago`. |
| `duration`, `durationText` | Length in seconds and as `m:ss` / `h:mm:ss`. |
| `channelId`, `channelName`, `channelUrl`, `channelHandle`, `subscriberCount` | The uploader. Subscriber counts are YouTube's rounded figures. |
| `hashtags`, `thumbnailUrl`, `isLive`, `isShort` | Hashtags from the title area and description, a stable thumbnail link, live and Shorts flags. |
| `detail` | `full` for rows with full statistics, `listing` for search and channel result rows. |
| `searchQuery` | The search term, on search rows. |

**Channel rows**: `channelId`, `channelName`, `channelUrl`, `channelHandle`, `description`, `subscriberCount`, `subscriberCountText`, `videoCount`, `viewCount` (total channel views), `joinedDate`, `country`, `links`, `keywords`, `avatarUrl`, `isFamilySafe` and `aboutComplete`.

**Comment rows**: `commentId`, `videoId`, `videoUrl`, `text`, `authorName`, `authorChannelId`, `authorIsChannelOwner`, `authorIsVerified`, `likeCount` (rounded the way YouTube shows it), `replyCount`, `publishedText`, `isEdited`, `isPinned`, `isReply` and `parentCommentId`.

The `OUTPUT` record of the run's key-value store summarizes the run: row counts by type, the run status and an `errors` list naming each input that could not be collected, with a `code` and a readable `message`. Inputs that fail never become dataset rows and are never charged.

### Output example

Illustrative rows (live values change constantly):

```jsonl
{"type": "video", "input": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "scrapedAt": "2026-10-19T22:36:59Z", "videoId": "dQw4w9WgXcQ", "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "title": "Rick Astley - Never Gonna Give You Up (Official Video) (4K Remaster)", "description": "The official video for “Never Gonna Give You Up” by Rick Astley. ...", "viewCount": 1828414467, "likeCount": 19489750, "commentCount": 2400000, "publishedAt": "2009-10-24", "publishedText": "Oct 24, 2009", "duration": 214, "durationText": "3:34", "channelId": "UCuAXFkgsw1L7xaCfnd5JJOw", "channelName": "Rick Astley", "channelUrl": "https://www.youtube.com/channel/UCuAXFkgsw1L7xaCfnd5JJOw", "channelHandle": null, "subscriberCount": 4550000, "hashtags": ["RickAstley", "NeverGonnaGiveYouUp"], "thumbnailUrl": "https://i.ytimg.com/vi/dQw4w9WgXcQ/hqdefault.jpg", "isLive": false, "isShort": null, "detail": "full"}
{"type": "channel", "input": "https://www.youtube.com/@mkbhd", "scrapedAt": "2026-10-19T22:37:02Z", "channelId": "UCBJycsmduvYEL83R_U4JriQ", "channelName": "Marques Brownlee", "channelUrl": "https://www.youtube.com/@mkbhd", "channelHandle": "@mkbhd", "description": "MKBHD: Quality Tech Videos | YouTuber | Geek | Consumer Electronics | Tech Head | Internet Personality!", "subscriberCount": 21400000, "subscriberCountText": "21.4M subscribers", "videoCount": 1859, "viewCount": 5798311104, "joinedDate": "2008-03-21", "country": "United States", "links": [{"title": "Twitter", "url": "https://twitter.com/MKBHD"}], "keywords": ["MKBHD", "MarquesBrownlee"], "avatarUrl": "https://yt3.googleusercontent.com/qu4TmIaYUlS41-dJ9gZ7DUR3nilvmB5_11i6OKSdvNnBNiyOusZP1bMN6ICnuxtjFBb6ioKgRQ=s900-c-k-c0x00ffffff-no-rj", "isFamilySafe": true, "aboutComplete": true}
{"type": "video", "input": "python tutorial", "scrapedAt": "2026-10-19T22:37:05Z", "videoId": "fWjsdhR3z3c", "url": "https://www.youtube.com/watch?v=fWjsdhR3z3c", "title": "Learn Python in Less than 10 Minutes for Beginners (Fast & Easy)", "description": "In this crash course I'll be teaching you the basics of Python in less than 10 minutes. ...", "viewCount": 1843779, "likeCount": null, "commentCount": null, "publishedAt": null, "publishedText": "5 years ago", "duration": 630, "durationText": "10:30", "channelId": "UCuudpdbKmQWq2PPzYgVCWlA", "channelName": "Indently", "channelUrl": "https://www.youtube.com/@Indently", "channelHandle": "@Indently", "subscriberCount": null, "hashtags": [], "thumbnailUrl": "https://i.ytimg.com/vi/fWjsdhR3z3c/hqdefault.jpg", "isLive": false, "isShort": false, "detail": "listing", "searchQuery": "python tutorial"}
{"type": "comment", "input": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "scrapedAt": "2026-10-19T22:37:08Z", "commentId": "UgzKYksqeQ5gA3t0g0F4AaABAg", "videoId": "dQw4w9WgXcQ", "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ", "text": "I Rick rolled my entire French class ...", "authorName": "@grustache4569", "authorChannelId": "UCKrzgYonJnMd0Dk7zQXjYXw", "authorIsChannelOwner": false, "authorIsVerified": false, "likeCount": 104000, "replyCount": 623, "publishedText": "6 years ago", "isEdited": true, "isPinned": false, "isReply": false, "parentCommentId": null}
```

### Limits and caveats

- Only public information is collected. Private, removed, members-only, age-restricted and region-restricted videos may be unavailable; they are listed in `OUTPUT.errors` and not charged.
- Uploader keyword tags are not exposed on public pages; `hashtags` lists the hashtags shown with the video instead.
- Search and channel rows show the upload age as YouTube displays it (`publishedText`); turn on `videoDetails` for an exact `publishedAt` date, likes and comment counts.
- Channel uploads cover the **Videos** tab; Shorts and past live streams on separate channel tabs are not included. Individual Shorts and live links can be added to `videoUrls`.
- Subscriber counts, comment like counts and comment counts above 1,000 are rounded by YouTube (for example `21.4M`).
- Comments can be turned off by the uploader; such videos are reported as `COMMENTS_UNAVAILABLE` and the video row is still delivered.
- A mixed run keeps its successful rows and lists failures in `OUTPUT.errors`. A run that delivers no row at all ends with an error.
- Use this actor only where you have the necessary permissions and a lawful basis; YouTube's terms restrict automated access.

Error codes you may see in `OUTPUT.errors`: `INVALID_INPUT`, `VIDEO_UNAVAILABLE`, `CHANNEL_NOT_FOUND`, `CHANNEL_UNAVAILABLE`, `COMMENTS_UNAVAILABLE`, `NO_RESULTS`, `RATE_LIMITED`, `BLOCKED`, `UPSTREAM_ERROR`, `NETWORK_ERROR` and `SOURCE_CHANGED`.

### FAQ

**Can I mix videos, channels and searches in one run?** Yes. Fill any combination of `videoUrls`, `channelUrls` and `searchQueries`; duplicates are delivered once.

**How do I get comments for videos found by a search?** Run the search first, then pass the video links you want to `videoUrls` with `maxComments` set.

**How do I keep the cost predictable?** Set `maxItems`. The run stops as soon as that many rows have been delivered.

**Do failed inputs cost anything?** No row charge applies to inputs that fail. The run-start charge still applies.

### Price

**$0.00125 per delivered row** (video, channel or comment), plus **$0.005 per run start**. A start is charged once per GB of memory, so the default 512 MB run is one start. Inputs that fail are not dataset rows and incur no row charge; the run-start charge still applies. Platform usage is included in these prices.

# Actor input Schema

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

Watch, youtu.be, Shorts or live links, or bare 11-character video IDs. Each becomes a full video row; add comments with the comment settings below.

## `channelUrls` (type: `array`):

For example https://www.youtube.com/@mkbhd, @mkbhd or UCBJycsmduvYEL83R_U4JriQ. Each channel adds a profile row plus its latest uploads.

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

Search YouTube as a visitor would and collect the matching videos in result order.

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

How many videos to collect for each search term and for each channel's uploads. Set 0 on a channel run to collect only the channel profiles.

## `videoDetails` (type: `boolean`):

Off: search and channel videos carry the result-list fields (title, views, duration, upload age, channel). On: each one also gets likes, comment count, exact publish date, subscribers and the full description. Slower.

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

Keep Shorts that appear inside search results.

## `includeChannelInfo` (type: `boolean`):

Add one profile row per channel: subscribers, total views, video count, join date, country, description and links.

## `maxComments` (type: `integer`):

Comments to collect for each video in Video links (0 = none). Replies count toward this limit when enabled.

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

Top comments (YouTube's ranking) or newest first.

## `includeReplies` (type: `boolean`):

Also collect replies under each comment, marked with the parent comment ID.

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

Hard stop across all inputs and row types. Use it to cap the run's cost.

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

The default works for most runs. Change it only if you need a specific country or your own proxies. Use a direct connection, Apify datacenter proxy or your own proxy URLs; other Apify proxy groups are refused.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "channelUrls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "searchQueries": [
    "python tutorial"
  ],
  "maxResults": 10,
  "videoDetails": false,
  "includeShorts": true,
  "includeChannelInfo": true,
  "maxComments": 0,
  "commentsSort": "top",
  "includeReplies": false,
  "maxItems": 1000,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}
```

# 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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ],
    "channelUrls": [
        "https://www.youtube.com/@mkbhd"
    ],
    "searchQueries": [
        "python tutorial"
    ],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("barefoot_grade/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 = {
    "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
    "channelUrls": ["https://www.youtube.com/@mkbhd"],
    "searchQueries": ["python tutorial"],
    "maxResults": 10,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("barefoot_grade/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 '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "channelUrls": [
    "https://www.youtube.com/@mkbhd"
  ],
  "searchQueries": [
    "python tutorial"
  ],
  "maxResults": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyCountry": "US"
  }
}' |
apify call barefoot_grade/youtube-scraper --silent --output-dataset

```

## MCP server setup

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