# YouTube Channel Scraper - Videos, Shorts & Comments (`skcho/youtube-channel-scraper-pro`) Actor

One channel, one run: the profile with subscriber and view counts, every long-form video and Short with exact views, likes, duration and upload date, and optionally the comments underneath them.

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

## Pricing

Pay per usage

This Actor is paid per platform usage. The Actor is free to use, and you only pay for the Apify platform usage, which gets cheaper the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-usage

## 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 Channel Scraper

The whole channel in one run: profile, every video, and optionally the comments
underneath them.

Start from a handle and end with a dataset you can hand straight to a dashboard —
subscriber counts, video catalogue, engagement, and audience voice, all keyed
together.

### Common uses

- **YouTube channel scraper**: subscriber count, total views, video catalogue and upload dates for one or many channels
- **YouTube Data API alternative**: no API key and no daily quota to manage, results as JSON, CSV or Excel
- **YouTube Shorts and long-form scraper**: both tabs in a single run
- **Competitor and influencer analysis**: compare channels by views, engagement and upload cadence
- **Comment and audience research**: collect comments per video, joined to the channel and video rows

### What you get

Three record shapes in one dataset, tagged by `recordType` so they separate with
a single filter.

**`channel`** — the profile

```json
{
  "recordType": "channel",
  "channelName": "Marques Brownlee",
  "channelId": "UCBJycsmduvYEL83R_U4JriQ",
  "handle": "@mkbhd",
  "subscriberCount": 20100000,
  "videoCount": 1784,
  "viewCount": 4692518841,
  "description": "Quality Tech Videos...",
  "keywords": ["tech", "reviews"],
  "isVerified": true,
  "country": "US",
  "joinedDateText": "Mar 21, 2008",
  "externalLinks": [{ "title": "Website", "url": "https://mkbhd.com" }]
}
```

**`video`** — one per upload

```json
{
  "recordType": "video",
  "videoId": "kYfNvmF0Bqw",
  "title": "The Best Smartphone Camera 2025",
  "type": "LONG_FORM",
  "uploadDateText": "3 weeks ago",
  "viewCount": 4821904,
  "likeCount": 79605,
  "durationText": "17:23",
  "thumbnailUrl": "https://i.ytimg.com/vi/kYfNvmF0Bqw/hqdefault.jpg"
}
```

**`comment`** — when `collectComments` is on, carrying the channel keys so it
joins back without a lookup table.

### Input

| Field | Meaning |
|---|---|
| `channelUrls` | One or many. `@handle`, `/channel/UC…`, or a bare handle |
| `collectChannelInfo` | Fetch the profile record |
| `includeVideos` / `videoType` | Video catalogue, and which tab: `ALL`, `LONG_FORM` or `SHORTS` |
| `maxItemsPerChannel` | Per-channel budget |
| `fetchLikes` | Exact like counts (an extra pass — slower, richer) |
| `minViews` / `minLikes` / `maxDaysOld` | Filters applied while crawling |
| `collectComments` + `maxCommentsPerVideo` | Audience voice per video |

```json
{
  "channelUrls": ["https://www.youtube.com/@mkbhd", "@veritasium"],
  "collectChannelInfo": true,
  "includeVideos": true,
  "videoType": "ALL",
  "minViews": 100000,
  "maxItemsPerChannel": 200,
  "fetchLikes": true
}
```

### How it holds up on long channels

Channel tabs are lazily loaded, so a naive scraper stops at the first screen.
This one takes a fast HTTP path when the page will give up its data that way, and
falls back to a real browser scroll when it won't — including when the fast path
returns nothing at all, which is exactly the case that produces silent empty runs
elsewhere.

Filters are applied **during** the crawl, not after: `maxDaysOld` and
`minViews` stop the scroll early instead of collecting thousands of rows and
throwing most away.

### Good for

- Competitive intelligence across a set of channels
- Pulling long-form videos and Shorts together, with subscribers and upload dates on every row
- Content calendars and upload-cadence analysis
- Finding a channel's outliers by views-per-day
- One-shot exports for BI tools, with channel/video/comment already joined

### Notes

- `maxCommentsPerVideo` is an upper bound — YouTube stops paginating comment
  threads early, on every tool, not just this one.
- `maxRepliesPerComment` (default 10, `0` = no cap) stops one viral comment from
  spending the whole per-video budget on its own reply thread. At the default a
  120-row request comes back as ~13 different comments with replies attached
  rather than a single comment and 119 of its replies.
- `fetchLikes` costs an extra request per video; leave it off for large catalogues
  unless you need exact counts.

### Which one do I need?

| You want | Use |
|---|---|
| The channel profile plus its video list, and the comments under those videos, in one dataset | **this actor** |
| The deepest metadata for every video — description, tags, category, chapters, caption languages | [YouTube Video Scraper](https://apify.com/skcho/youtube-video-metadata-collector) |
| Videos matching a keyword rather than a channel | [YouTube Search Scraper](https://apify.com/skcho/youtube-scraper-enterprise) |

### More actors from this author

- [YouTube Search Scraper](https://apify.com/skcho/youtube-scraper-enterprise) — keyword search to videos with exact views, likes and publish dates
- [YouTube Channel Search](https://apify.com/skcho/youtube-channel-discovery) — find channels and creators by keyword, with subscriber counts
- [YouTube Video Scraper](https://apify.com/skcho/youtube-video-metadata-collector) — every video on a channel with tags, chapters and captions
- [YouTube Comments Scraper](https://apify.com/skcho/youtube-comment-collector) — comments and reply threads from any video URL

### Questions and problems

Open an issue on the **Issues** tab of this actor, with the run ID.

# Actor input Schema

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

List of YouTube channel URLs, channel IDs, or @handles to collect channel profile info from.

## `collectChannelInfo` (type: `boolean`):

Collect channel profile metadata such as name, description, subscribers, total views, video count, joined date, country, avatar, banner, and links.

## `includeVideos` (type: `boolean`):

Also collect video rows from the channel videos/shorts tabs. Leave disabled when you only need channel profile info.

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

Type of videos to scrape.

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

Filter videos uploaded ON or AFTER this date. Format: YYYY-MM-DD. Leave empty to ignore.

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

Filter videos uploaded ON or BEFORE this date. Format: YYYY-MM-DD. Leave empty to ignore.

## `minViews` (type: `integer`):

Only scrape videos with at least this many views.

## `fetchLikes` (type: `boolean`):

If enabled, the scraper will visit each video page individually to fetch like counts. WARNING: This will significantly increase scraping time.

## `minLikes` (type: `integer`):

Only scrape videos with at least this many likes.

## `maxItemsPerChannel` (type: `integer`):

Maximum number of videos to scrape per channel (0 for unlimited).

## `maxDaysOld` (type: `integer`):

Stop scraping videos uploaded older than this many days. Leave 0 to disable.

## `proxyUrls` (type: `array`):

One proxy URL per line (e.g. http://user:pass@host:port), rotated across requests. Recommended for anything beyond a handful of channels — without a proxy, YouTube can rate-limit or temporarily block your IP. Leave empty to run without a proxy.

## `collectComments` (type: `boolean`):

Requires "Include Video Rows". Collects comments (and optionally replies) for each collected video as separate comment records.

## `maxCommentsPerVideo` (type: `integer`):

Maximum number of comments (including replies, if enabled) to collect per video.

## `includeCommentReplies` (type: `boolean`):

Also collect replies to each top-level comment. Videos with many replies will take longer.

## `maxRepliesPerComment` (type: `integer`):

Cap on replies taken from any single comment, so one huge thread cannot consume the whole per-video budget. 0 = no cap.

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

Order to collect comments in.

## Actor input object example

```json
{
  "channelUrls": [
    "https://www.youtube.com/@mkbhd",
    "@veritasium"
  ],
  "collectChannelInfo": true,
  "includeVideos": false,
  "videoType": "ALL",
  "minViews": 0,
  "fetchLikes": false,
  "minLikes": 0,
  "maxItemsPerChannel": 5,
  "maxDaysOld": 0,
  "proxyUrls": [],
  "collectComments": false,
  "maxCommentsPerVideo": 50,
  "includeCommentReplies": true,
  "maxRepliesPerComment": 10,
  "commentSortBy": "TOP_COMMENTS"
}
```

# Actor output Schema

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

Every record the run produced, tagged by recordType: one channel row, one row per video, and comment rows when collectComments is on.

# 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 = {
    "channelUrls": [
        "https://www.youtube.com/@mkbhd",
        "@veritasium"
    ],
    "minUploadDate": "",
    "maxUploadDate": "",
    "proxyUrls": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("skcho/youtube-channel-scraper-pro").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 = {
    "channelUrls": [
        "https://www.youtube.com/@mkbhd",
        "@veritasium",
    ],
    "minUploadDate": "",
    "maxUploadDate": "",
    "proxyUrls": [],
}

# Run the Actor and wait for it to finish
run = client.actor("skcho/youtube-channel-scraper-pro").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 '{
  "channelUrls": [
    "https://www.youtube.com/@mkbhd",
    "@veritasium"
  ],
  "minUploadDate": "",
  "maxUploadDate": "",
  "proxyUrls": []
}' |
apify call skcho/youtube-channel-scraper-pro --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,skcho/youtube-channel-scraper-pro"
        }
    }
}
```

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/omN8wBHwUe0g28QbS/builds/qua89Z4mFY6Pv3GWs/openapi.json
