# YouTube Channel Scraper - Videos & Shorts (`gganbukim/youtube-channel-scraper`) Actor

Scrape every video from any YouTube channel with views, duration, publish date and thumbnails, plus a channel summary row: subscribers, total video count, description, keywords, avatar and RSS feed. Long-form uploads, Shorts and past live streams. Date and view filters are free.

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

## Pricing

from $0.50 / 1,000 video rows

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/platform/actors/running/actors-in-store#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 Channel Scraper — All Videos, Shorts & Channel Stats

Give it a channel, get back **the whole back catalogue**: every video with views, duration, publish date and thumbnail, plus a channel summary row with subscriber count, total videos, description, keywords, avatar and RSS feed URL.

Works with `@handles`, `/channel/UC…` URLs, and legacy `/c/` and `/user/` URLs. Multiple channels per run.

### What it does well

- **Videos, Shorts and past live streams** — pick one tab or scrape all of them.
- **Full pagination** — the entire catalogue, not the first 30 videos.
- **Server-side filters** — newer than N days, above N views, or under N seconds. **Filtered videos are not billed.**
- **Shorts get the same depth as videos** — the Shorts shelf publishes only id, title and views, so **Include details** fetches each Short's real length, exact date, description and keywords.
- **Channel summary row** — subscribers, video count, description, keywords, avatar, RSS URL.
- **Cheap and stable** — this actor only reads listing pages, so it stays on datacenter proxies and rarely gets throttled.

### Input

```jsonc
{
  "channelUrls": ["@veritasium", "https://www.youtube.com/@3blue1brown"],
  "tab": "videos",
  "maxVideosPerChannel": 500,
  "publishedWithinDays": 90,
  "minViewCount": 10000
}
```

#### Every option

The same wording you see in the Apify console, with the JSON key for API and MCP callers.

| Option | What it does | Default |
|---|---|---|
| **Channels** — `channelUrls` *(required)* | @handles, /channel/UC… URLs, or legacy /c/ and /user/ URLs. One row per channel plus one row per video is written to the dataset. | — |
| **Which videos** — `tab` | Long-form uploads, Shorts, past live streams, or everything. | `"videos"` |
| **Max videos per channel** — `maxVideosPerChannel` | Videos arrive 30 per request, so this is the main cost control. Leave empty for the full back catalogue. | `200` |
| **Only videos newer than (days)** — `publishedWithinDays` | 0 disables the filter. YouTube only exposes relative dates on listing pages ("3 weeks ago"), so this is approximate to within a day. Filtered videos are not billed. | `0` |
| **Minimum views** — `minViewCount` | Drop videos below this view count. Filtered videos are not billed. | `0` |
| **Maximum length (seconds)** — `maxDurationSeconds` | 0 disables. On the Shorts tab this needs **Include details** on, because the shelf publishes no length there — it separates true short-form from the 3-minute Shorts YouTube now allows. Long-form videos carry their length already. Filtered videos are not billed. | `0` |
| **Include a channel summary row** — `includeChannelRow` | Write one row per channel with subscribers, total video count, description, keywords, avatar and RSS URL. | `true` |
| **Include details (length, exact date, description)** — `includeDetails` | Spends one extra request per row to fetch real length, exact publish date, description and keywords. Shorts need it — that shelf publishes only id, title and views — while long-form videos already arrive with length and an approximate date. The date costs one further request, because YouTube returns the upload date only to a different client. | `false` |
| **Concurrency** — `concurrency` | How many channels to read in parallel. | `3` |
| **Proxy** — `proxyConfiguration` | Leave the default. This actor only reads listing pages, so it almost always stays on cheap datacenter proxies. | `{"useApifyProxy":true}` |

### Output

Two row types share one dataset, told apart by `type`.

One channel row:

```json
{
  "type": "channel",
  "channelId": "UCHnyfMqiRRG1u-2MsSQLbXA",
  "channelName": "Veritasium",
  "channelUrl": "https://www.youtube.com/channel/UCHnyfMqiRRG1u-2MsSQLbXA",
  "handle": "@veritasium",
  "handleUrl": "https://www.youtube.com/@veritasium",
  "subscriberCount": 21100000,
  "subscriberCountText": "21.1M subscribers",
  "videoCount": 526,
  "description": "An element of truth - videos about science...",
  "keywords": "\"science videos\" science \"science education\"...",
  "avatarUrl": "https://yt3.googleusercontent.com/...",
  "rssUrl": "https://www.youtube.com/feeds/videos.xml?channel_id=UCHnyfMqiRRG1u-2MsSQLbXA",
  "videosFound": 500
}
```

Then one row per video:

```json
{
  "type": "video",
  "videoId": "wt4p2oalmRY",
  "url": "https://www.youtube.com/watch?v=wt4p2oalmRY",
  "title": "Is spider web really stronger than steel?",
  "viewCount": 5500000,
  "durationSeconds": 1618,
  "publishedText": "9 days ago",
  "approxAgeDays": 9,
  "thumbnailUrl": "https://i.ytimg.com/vi/wt4p2oalmRY/hqdefault.jpg",
  "isShort": false,
  "tab": "videos",
  "channelId": "UCHnyfMqiRRG1u-2MsSQLbXA",
  "channelName": "Veritasium",
  "channelUrl": "https://www.youtube.com/channel/UCHnyfMqiRRG1u-2MsSQLbXA"
}
```

#### Every field

You are billed per row, so here is everything a row can contain. A field is absent when
YouTube did not publish it for that channel or video.

| Field | Rows | What it is |
|---|---|---|
| `type` | both | `"channel"` or `"video"` — how you split the two shapes apart. |
| `channelId` | both | The `UC…` id. Stable; handles are not. |
| `channelName` | both | Display name. |
| `channelUrl` | both | Canonical `/channel/UC…` URL, built from the id. |
| `handle` | channel | The `@handle`, when the channel has one. |
| `handleUrl` | channel | `youtube.com/@handle`, or the owner URL when there is no handle. |
| `description` | channel | The channel's About text. |
| `keywords` | channel | The channel's own keyword string, as YouTube stores it. |
| `subscriberCount` | channel | Parsed from the display text, so it is rounded the way YouTube rounds it. |
| `subscriberCountText` | channel | The unparsed original, e.g. `"21.1M subscribers"`. |
| `videoCount` | channel | Total uploads the channel reports — not the number scraped. |
| `avatarUrl` | channel | Largest available avatar. |
| `rssUrl` | channel | The channel's RSS feed, useful for cheap polling between runs. |
| `videosFound` | channel | How many videos this run actually returned for the channel. |
| `videoId` | video | The 11-character id. |
| `url` | video | `watch?v=` URL. |
| `title` | video | Video title. |
| `publishedText` | video | YouTube's relative text, e.g. `"9 days ago"` — see the note below. Never present on Shorts. |
| `approxAgeDays` | video | Age in days: from `publishedAt` when details are on, otherwise `publishedText` converted, accurate to about a day. |
| `publishedAt` | video | Exact upload timestamp. **Only with `includeDetails`.** |
| `durationSeconds` | video | Length in seconds. |
| `viewCount` | video | Views, parsed from the display text. |
| `thumbnailUrl` | video | Largest available thumbnail. |
| `isShort` | video | `true` when YouTube served the item as a Short. |
| `tab` | video | Which tab it came from: `"videos"`, `"shorts"` or `"live"`. |

### A note on dates and Shorts

YouTube's listing pages only expose **relative** publish text ("3 weeks ago"), not exact timestamps. `approxAgeDays` converts that for filtering and sorting, but it is accurate to about a day. Switch on **Include details** for an exact `publishedAt`, at one extra request per row.

**The Shorts shelf is thinner than the videos grid**, and it changes what the filters can do. Measured across two channels, twenty Shorts each: `videoId`, `url`, `title` and `viewCount` came back 20/20, while `durationSeconds`, `publishedText` and `thumbnailUrl` came back **0/20**. So on the Shorts tab:

- **Minimum views** works as usual — view count is on the shelf.
- **Only videos newer than** and **Maximum length** need **Include details**, and the run stops with an explanation if you set one without it. A filter that can never fire is worse than no filter.
- On **All tabs**, a date or length filter with details off drops every Short and warns you, rather than quietly passing them through unchecked.

### Who this is for

- **Competitive analysts** — snapshot a rival weekly and diff the results — what they published, what got views, what length they settled on.
- **Influencer sourcers** — subscriber counts and output volume across a list of channels, in one table.
- **Data teams feeding other tools** — video URLs straight into a transcript, comments or sponsorship run.
- **Archivists** — a structured record of a catalogue as it changes.

### Common uses

- **Competitor tracking** — snapshot a rival channel weekly and diff the results.
- **Content research** — find which topics and lengths actually get views in a niche.
- **Influencer sourcing** — pull subscriber counts and output volume across a list of channels.
- **Feeding other tools** — take the video URLs straight into a transcript or comments run.
- **Archiving** — keep a structured record of a catalogue over time.

### Pricing

Pay per row delivered — one event per channel summary, one per video. Videos removed by your date or view filters, and failed channels, **cost nothing**.

Starting a run costs $0.00002 — the platform's $0.00001 minimum, charged once per GB of memory, and these Actors run on 2 GB. That is two cents per thousand runs, and it is the only charge not tied to a row you received.

### Other Actors in this family

Same engines, same billing, no account or API key on any of them.

**YouTube & video**

- [Download YouTube Subtitles in Bulk — SRT, VTT & Text](https://apify.com/gganbukim/youtube-transcript-scraper) — Bulk subtitles from videos, channels or playlists — text, SRT, VTT or RAG chunks.
- [Export YouTube Comments to CSV — Replies and Likes](https://apify.com/gganbukim/youtube-comments-scraper) — Every comment and reply thread, with likes, authors and creator flags.
- [Find YouTube Sponsors — Brand Deals, Codes & Links](https://apify.com/gganbukim/youtube-sponsorship-finder) — Which brands pay which creators, with the campaign link, the code and the timestamp.
- [YouTube Search API — Bulk Results, No Quota](https://apify.com/gganbukim/youtube-search-scraper) — Many search terms at once, every result as a row, filtered before you are billed.
- [Track Deleted YouTube Videos & Title Changes](https://apify.com/gganbukim/youtube-channel-monitor) — What a channel quietly changed: deleted videos, rewritten titles, view velocity.
- [YouTube Creator Email Finder & Sponsor Lookup](https://apify.com/gganbukim/youtube-creator-leads) — A channel list into leads: the published email, audience bands, and who already sponsors them.
- [Export a YouTube Playlist to CSV — Every Video](https://apify.com/gganbukim/youtube-playlist-scraper) — Any playlist as a table, with each video position in it.

**Search demand**

- [AnswerThePublic Alternative — Autocomplete Keyword API](https://apify.com/gganbukim/long-tail-keyword-scraper) — One seed into hundreds of real keywords from Google, YouTube and Amazon autocomplete.
- [Google Trends API — Today's Trending Searches, No Key](https://apify.com/gganbukim/google-trends-scraper) — Today's trending searches by country, with traffic bands and the news behind them.

**E-commerce**

- [Export Any Shopify Store's Products to CSV or JSON](https://apify.com/gganbukim/shopify-product-scraper) — Any Shopify catalogue: variants, SKUs, live prices, stock, images, collections.
- [New Shopify Product Alerts — Competitor Drop Tracker](https://apify.com/gganbukim/shopify-new-arrivals-monitor) — Only what a store launched since the last run. Scanning is free.

**Hiring**

- [Greenhouse, Lever & Ashby Job Scraper — No API Key](https://apify.com/gganbukim/ats-job-scraper) — Paste a company domain, get its open roles from Greenhouse, Ashby, Lever or SmartRecruiters.
- [Ghost Job Detector — Track Reposts, Closures & Edits](https://apify.com/gganbukim/job-lifecycle-monitor) — What changed on a careers page: opened, closed, quietly reposted, or a ghost job.

### FAQ

**Will I get blocked, or my account banned?** There is no account to ban — no login, no cookies. This one only reads listing pages, which are the cheapest thing YouTube serves and the least defended, so it usually finishes the whole back catalogue without leaving datacenter proxies. YouTube signals throttling with an HTTP 200 and an error body rather than an error code, which is the trap that silently halves most scrapers' results; this one detects it by reason and rotates.

**Does it need a YouTube API key?** No. The Data API caps at 10,000 quota units/day, which is roughly 100 channel listings; this has no such limit.

**Can I get exact publish dates?** Not from listing pages — see the note above.

**Can I run it on a schedule?** Yes, via Apify Schedules, webhooks, or the API. Also available over MCP for AI agents.

**Is it legal to scrape a YouTube channel's video list?** This reads listing pages YouTube
serves publicly, with no login and nothing bypassed, and the fields are facts about
videos — titles, view counts, dates — rather than the videos themselves. Public-data
scraping is broadly treated as lawful in the US; YouTube's Terms of Service are a separate
contract question. Not legal advice.

**How much does 1,000 videos cost?** $0.50, plus $5.00 per 1,000 channel summary rows and
$0.00002 for the run. Videos your date or view filters remove are never billed.

**Can I export the results to Excel or Google Sheets?** Yes. Every run's dataset
downloads as CSV, Excel, JSON, XML or RSS from the Storage tab, or straight from the API
if you want a live link a spreadsheet can pull.

**Can I connect it to Zapier, Make or n8n?** Yes — Apify publishes integrations for all
three, plus webhooks that fire when a run finishes. A common setup is a schedule here and
a webhook into your own database or Slack.

**Do I need to write code?** No. Fill the form in the console and press Start. If you do
want code, the Apify client libraries for Python and JavaScript call this the same way,
and it is available over MCP so an AI agent can call it directly.

# Actor input Schema

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

@handles, /channel/UC… URLs, or legacy /c/ and /user/ URLs. One row per channel plus one row per video is written to the dataset.

## `tab` (type: `string`):

Long-form uploads, Shorts, past live streams, or everything.

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

Videos arrive 30 per request, so this is the main cost control. Leave empty for the full back catalogue.

## `publishedWithinDays` (type: `integer`):

0 disables the filter. YouTube only exposes relative dates on listing pages ("3 weeks ago"), so this is approximate to within a day. On the Shorts tab it needs Include details switched on, because the Shorts shelf publishes no date at all. Filtered videos are not billed.

## `minViewCount` (type: `integer`):

Drop videos below this view count. Filtered videos are not billed.

## `maxDurationSeconds` (type: `integer`):

0 disables. On the Shorts tab this needs Include details on, because the shelf publishes no length there - it separates true short-form from the 3-minute Shorts YouTube now allows. Long-form videos carry their length already. Filtered videos are not billed.

## `includeChannelRow` (type: `boolean`):

Write one row per channel with subscribers, total video count, description, keywords, avatar and RSS URL.

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

Spends one extra request per row to fetch real length, exact publish date, description and keywords. Shorts need it - that shelf publishes only id, title and views - while long-form videos already arrive with length and an approximate date. The date costs one further request, because YouTube returns the upload date only to a different client.

## `concurrency` (type: `integer`):

How many channels to read in parallel.

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

Leave the default. This actor only reads listing pages, so it almost always stays on cheap datacenter proxies.

## Actor input object example

```json
{
  "channelUrls": [
    "@veritasium"
  ],
  "tab": "videos",
  "maxVideosPerChannel": 200,
  "publishedWithinDays": 0,
  "minViewCount": 0,
  "maxDurationSeconds": 0,
  "includeChannelRow": true,
  "includeDetails": false,
  "concurrency": 3,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

One row per video, plus a channel summary row per channel when that option is on.

## `runSummary` (type: `string`):

Counts for this run: rows delivered, rows filtered, items that failed, the proxy tier used, and whether the run stopped at its charge limit.

# 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": [
        "@veritasium"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("gganbukim/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 = { "channelUrls": ["@veritasium"] }

# Run the Actor and wait for it to finish
run = client.actor("gganbukim/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 '{
  "channelUrls": [
    "@veritasium"
  ]
}' |
apify call gganbukim/youtube-channel-scraper --silent --output-dataset

```

## MCP server setup

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