# YouTube Channel Scraper - Subscribers, Stats & All Videos (`santhej/youtube-channel-scraper`) Actor

Scrape any YouTube channel without an API key or quota: subscribers, total views, description, links and banner, plus every upload with exact view counts, likes, comments and publish dates. Paste channel URLs, @handles or channel IDs. Clean JSON/CSV.

- **URL**: https://apify.com/santhej/youtube-channel-scraper.md
- **Developed by:** [Santhej Kallada](https://apify.com/santhej) (community)
- **Categories:** Videos, Social media
- **Stats:** 1 total users, 0 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## Pricing

Pay per event

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

## Fast YouTube Channel Scraper — Subs, Stats & All Videos

Scrape any YouTube channel with **no YouTube API key and no quota**: subscriber count, exact video count, **lifetime channel views**, join date, country, banner and **every social link in the About panel** — plus **every upload** with **exact views, exact likes, exact comment count**, description, tags, category, duration in seconds and an **ISO-8601 publish timestamp**.

Paste `@handles`, channel URLs or channel IDs and export clean JSON/CSV.

Perfect for influencer research, competitor tracking, content audits, lead lists, ML datasets and feeding channel catalogs into LLMs.

### Why this one

Most YouTube channel scrapers read the numbers off the page the way a human sees them — `714K views`, `2 days ago` — which is rounded and unsortable. This one goes one step further and pulls the **real** numbers straight from YouTube's own metadata.

| | Rounded listing (what most tools return) | This actor with enrichment ON |
|---|---|---|
| Views | `714000` (from "714K") | **`714699`** |
| Likes | — | **`34641`** |
| Comments | — | **`2264`** |
| Published | `"8 hours ago"` | **`"2026-08-24T20:27:50Z"`** — sortable |
| Description / tags / category | — | full text, `["MKBHD", …]`, `"Science & Technology"` |
| Channel lifetime views | — | **`5541785965`** |
| Channel social links | — | `[{ "title": "Twitter", "url": "http://twitter.com/MKBHD" }, …]` |

### Features

- Accepts **any channel identifier**: `@mkbhd`, `youtube.com/@mkbhd`, `/channel/UC…`, `/c/name`, `/user/name`, or a bare `UC…` channel ID.
- **Channel profile row**: title, description, keywords (flat *and* parsed array), subscriber count, **exact** video count, **lifetime total views**, **join date**, **country**, **verified badge**, avatar, **banner**, and every **link from the About panel** as `{title, url}` pairs — the highest-value field for lead-gen, since it is where creators put their email landing pages, Discords and shops.
- **Every upload** (newest first), following pagination up to **5,000 videos per channel**.
- **Exact per-video stats** (`enrichVideos`, on by default): views, likes, comments, description, tags, category, `duration_seconds` and ISO `published_at`.
- Parsed **numeric counts** *and* the raw display text, so nothing is lost.
- **Bulk**: up to 100 channels per run, scraped in parallel with polite, rate-limit-friendly paging.
- **Residential proxy** support to avoid IP blocks.
- **No YouTube Data API key, no quota, no login** — and channels that fail to resolve are reported as error rows and never charged.

### Input

| Field | Type | Description |
|---|---|---|
| `channels` | array | Channel `@handles`, URLs (any form) or `UC…` IDs, one per line. Max 100. **Required.** |
| `maxVideosPerChannel` | integer | Most recent uploads to return per channel. `0` = channel info only. Default `50`, max `5000`. |
| `enrichVideos` | boolean | Fetch exact per-video stats. Default **`true`**. See below. |
| `maxEnrichedVideos` | integer | Cap on enriched videos per channel. Default `200`, max `1000`, `0` disables. |
| `proxyConfiguration` | object | Residential proxy recommended (default). |

#### About `enrichVideos`

YouTube's channel listing only shows rounded values. To get the real ones, the actor makes a
small number of extra requests per video, so it is **billed separately** at `$0.0002` per
enriched video and capped by `maxEnrichedVideos`.

- **ON** (default) — the first `maxEnrichedVideos` videos of each channel get exact
  `views`, `likes`, `comment_count`, `description`, `tags`, `category`, `duration_seconds`
  and `published_at`, and are marked `enriched: true`.
- **OFF** — fast, cheapest listing only. Rows carry YouTube's rounded display values and
  `enriched: false`. Nothing extra is charged.
- Videos beyond the cap are still returned in full, just with `enriched: false`.
- If a video's enrichment fails (deleted, private, members-only, region-locked), the row is
  still returned with the display values and **is not charged** for enrichment.

#### Example input

```json
{
  "channels": ["@mkbhd", "https://www.youtube.com/@veritasium", "UCBJycsmduvYEL83R_U4JriQ"],
  "maxVideosPerChannel": 100,
  "enrichVideos": true,
  "maxEnrichedVideos": 200
}
```

### Output

Two row types, distinguished by `record_type`.

#### Channel row (`record_type: "channel"`)

| Field | Description |
|---|---|
| `channel_id` | Canonical `UC…` channel ID |
| `handle` | `@handle` |
| `title`, `description` | Channel profile text |
| `keywords` | Channel keywords as YouTube stores them (one flat string) |
| `keywords_list` | The same keywords parsed into an array, quoted phrases kept intact |
| `subscriber_count` / `subscriber_count_text` | `21100000` / `"21.1M subscribers"` |
| `video_count` / `video_count_text` | **`1843`** / `"1,843 videos"` — exact, from the About panel |
| `total_views` / `total_views_text` | **`5541785965`** / `"5,541,785,965 views"` — lifetime channel views |
| `joined_date` | `"2008-03-21"` — ISO date the channel was created |
| `country` | `"United States"` (null when the channel hides it) |
| `is_verified` | `true` when the channel carries the verified checkmark |
| `links` | Array of `{ title, url }` from the About panel — social profiles, shops, Discords |
| `avatar_url`, `banner_url` | Profile image and channel banner (largest available) |
| `canonical_url` | Channel URL |
| `is_family_safe` | YouTube's family-safe flag |
| `scraped_at` | ISO timestamp |

#### Video row (`record_type: "video"`)

| Field | Description |
|---|---|
| `video_id`, `url` | 11-char ID and watch URL |
| `title` | Video title |
| `views` | **Exact** view count when `enriched`, else parsed from the display text |
| `views_text` | Raw display text, e.g. `"714K views"` |
| `likes` | **Exact** like count — enriched rows only |
| `comment_count` | **Exact** comment count — enriched rows only |
| `published_at` | ISO-8601 UTC, e.g. `"2026-08-24T20:27:50Z"` — enriched rows only |
| `published_text` | `"8 hours ago"` |
| `duration_seconds` | `389` — always present (parsed from the badge, exact when enriched) |
| `duration_text` | `"6:29"` |
| `description` | Full video description — enriched rows only |
| `tags` | Array of the video's tags — enriched rows only |
| `category` | e.g. `"Science & Technology"` — enriched rows only |
| `thumbnail_url` | Thumbnail image |
| `enriched` | `true` when the exact stats above were fetched |
| `channel_id`, `channel_title` | Parent channel |
| `position` | 1 = newest upload |

#### Example output

```json
{
  "record_type": "channel",
  "channel_id": "UCBJycsmduvYEL83R_U4JriQ",
  "handle": "@mkbhd",
  "title": "Marques Brownlee",
  "subscriber_count": 21100000,
  "subscriber_count_text": "21.1M subscribers",
  "video_count": 1843,
  "total_views": 5541785965,
  "joined_date": "2008-03-21",
  "country": "United States",
  "is_verified": true,
  "keywords_list": ["MKBHD", "MarquesBrownlee", "Marques", "Brownlee"],
  "links": [
    { "title": "Twitter", "url": "http://twitter.com/MKBHD" },
    { "title": "Instagram", "url": "http://instagram.com/MKBHD" },
    { "title": "Discord", "url": "http://discord.gg/MKBHD" }
  ],
  "canonical_url": "https://www.youtube.com/@mkbhd"
}
```

```json
{
  "record_type": "video",
  "channel_id": "UCBJycsmduvYEL83R_U4JriQ",
  "channel_title": "Marques Brownlee",
  "video_id": "ngPkbaZliaU",
  "url": "https://www.youtube.com/watch?v=ngPkbaZliaU",
  "title": "The Truth About the Bezelless Concept Phone",
  "views": 729290,
  "views_text": "729K views",
  "likes": 35090,
  "comment_count": 2290,
  "published_at": "2026-08-24T20:27:50Z",
  "published_text": "8 hours ago",
  "duration_seconds": 389,
  "duration_text": "6:29",
  "category": "Science & Technology",
  "tags": ["bezelless smartphone", "bezelless", "bezels", "MKBHD"],
  "enriched": true,
  "position": 1
}
```

Channels that can't be resolved come back as `{ "record_type": "error", "input": "…", "error": "…" }` — the run continues and you are **not** charged for them.

### Pricing

Pay per event — no subscription, no YouTube API quota to buy:

| Event | Price |
|---|---|
| Actor start | $0.001 per run |
| Channel scraped | $0.005 per channel |
| Video row | $0.0001 per video ($0.10 / 1,000) |
| Video enriched with exact stats | $0.0002 per video ($0.20 / 1,000) |

**Worked examples**

| Run | Maths | Total |
|---|---|---|
| 1 channel, 50 videos, enrichment ON | 0.001 + 0.005 + 50×0.0001 + 50×0.0002 | **$0.021** |
| 1 channel, 500 videos, enrichment ON (capped at 200) | 0.001 + 0.005 + 500×0.0001 + 200×0.0002 | **$0.096** |
| 1 channel, 500 videos, enrichment OFF | 0.001 + 0.005 + 500×0.0001 | **$0.056** |
| 10 channels, 50 videos each, enrichment ON | 0.001 + 10×0.005 + 500×0.0001 + 500×0.0002 | **$0.201** |
| 10 channels, channel profiles only (`maxVideosPerChannel: 0`) | 0.001 + 10×0.005 | **$0.051** |

Enrichment is only ever charged for videos that were actually enriched.

### FAQ

**Do I need a YouTube API key?** No. No API key, no OAuth, no quota. Paste channels and run.

**Are the view counts exact?** With `enrichVideos` on, yes — `views` is the exact integer
(e.g. `729290`), not YouTube's rounded "729K". With it off, `views` is parsed from the display
text and is approximate; `views_text` always carries the raw string either way.

**Are subscriber counts exact?** No — YouTube itself only publishes rounded subscriber counts
("21.1M subscribers") to anyone, including its own API. `subscriber_count` is that value parsed
to a number. Video count and lifetime channel views **are** exact.

**How many videos can it get per channel?** Up to 5,000 (YouTube's Videos tab, newest first,
followed through pagination). Shorts and Live tabs are not included — these are the channel's
long-form uploads.

**Why cap enrichment at 200 by default?** Enrichment costs extra requests and extra time per
video. 200 covers roughly a year of uploads for most channels. Raise `maxEnrichedVideos` (max
1,000) if you need deeper history, or set it to `0` to skip enrichment entirely.

**What about channels with hidden subscribers?** The channel row is still returned with
`subscriber_count: null`.

**Some rows have `likes: null`.** A small number of videos (age-restricted, members-only,
region-locked, or with the like count hidden) do not expose every stat. Those fields come back
`null` rather than guessed, and the row still carries whatever was available.

**Does it work with old /c/ and /user/ URLs?** Yes — every URL form is resolved to the canonical
channel ID first.

**What happens with a wrong handle?** You get an error row for that input, the rest of the run
continues, and you're not charged for it.

### Related actors

- [YouTube Transcript Scraper](https://apify.com/santhej/youtube-transcript-scraper) — full captions/subtitles of any video as clean text.
- [YouTube Comments Scraper](https://apify.com/santhej/youtube-comments-scraper) — comments with authors, likes and reply counts.
- [YouTube Channel Email Scraper](https://apify.com/santhej/youtube-channel-email-scraper) — contact emails and social links from channels.

*Tags: youtube channel scraper, youtube subscriber count, youtube video list, exact view count, youtube like count, channel statistics, youtube without api key, youtube data export, influencer research, competitor analysis, youtube channel videos, youtube metadata*

# Actor input Schema

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

Channels to scrape, one per line. Accepts @handles (@mkbhd), any channel URL (youtube.com/@mkbhd, /channel/UC…, /c/name, /user/name) or bare channel IDs (UC…). Max 100 per run.

## `maxVideosPerChannel` (type: `integer`):

How many of the channel's most recent uploads to return per channel (newest first). Set 0 for channel info only, no video rows. Max 5000.

## `enrichVideos` (type: `boolean`):

ON: every video also gets its EXACT view count (714,699 — not the rounded "714K" YouTube displays), exact like count, exact comment count, full description, tags, category, duration in seconds and an ISO-8601 publish timestamp you can sort and filter on. Costs $0.0002 per enriched video. OFF: fast listing only, with YouTube's rounded display values.

## `maxEnrichedVideos` (type: `integer`):

Caps how many videos get the exact-stats treatment, so a 5,000-video channel stays fast. The first N videos in listing order are enriched; the rest keep the rounded display values and are marked enriched: false. Only applies when the option above is ON.

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

Residential proxy is recommended to avoid YouTube IP blocks.

## Actor input object example

```json
{
  "channels": [
    "@mkbhd"
  ],
  "maxVideosPerChannel": 10,
  "enrichVideos": true,
  "maxEnrichedVideos": 10,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

One record per video with transcript text and metadata.

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

Counts of videos processed and transcripts returned.

# 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 = {
    "channels": [
        "@mkbhd"
    ],
    "maxVideosPerChannel": 10,
    "enrichVideos": true,
    "maxEnrichedVideos": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("santhej/youtube-channel-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 = {
    "channels": ["@mkbhd"],
    "maxVideosPerChannel": 10,
    "enrichVideos": True,
    "maxEnrichedVideos": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("santhej/youtube-channel-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 '{
  "channels": [
    "@mkbhd"
  ],
  "maxVideosPerChannel": 10,
  "enrichVideos": true,
  "maxEnrichedVideos": 10
}' |
apify call santhej/youtube-channel-scraper --silent --output-dataset

```

## MCP server setup

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