# YouTube Channel Scraper (`mlg14/youtube-channel-scraper`) Actor

Collect public YouTube channel profiles, videos, Shorts, and streams. Export channel statistics, video titles, views, dates, durations, thumbnails, and optional descriptions.

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

## Pricing

$1.00 / 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 Channel Scraper

Scrape public YouTube channel profiles, regular videos, Shorts, and streams from one or many channel URLs. Export YouTube channel data to CSV, JSON, or Excel, including subscriber counts, channel views, video titles, view counts, dates, durations, thumbnails, and optional video descriptions. It works as a YouTube API alternative for public channel research without requiring a data API key.

A channel URL is enough to start. Set a separate limit for each video type, choose Newest, Popular, or Oldest, and receive one dataset row per result. Set all three type limits to zero when you need one profile row per channel. The output uses stable field names so recurring runs can be compared or loaded into a database.

### What data can you extract from YouTube?

Each video row repeats the public channel fields. This makes CSV exports useful without joining a separate profile table. Counts displayed in shortened form by YouTube are approximate; the original label is retained beside the numeric value. Fields that the site does not expose for a particular row are `null` or an empty list.

| Field | Description | Example |
| --- | --- | --- |
| `id` | Video ID, or channel ID in profile-only mode | `IwZVXmQdX1E` |
| `title` | Video, Short, or stream title | `NASA Moon Base: The First Six Months` |
| `duration` | List-page duration where shown | `1:07` |
| `date` | Date label displayed on the channel tab | `2w ago` |
| `publishedAt` | Exact date when video details are enabled and available | `2026-09-08` |
| `url` | Direct public URL of the result | `https://www.youtube.com/watch?v=IwZVXmQdX1E` |
| `viewCount` | Numeric view count, approximate unless detail data supplies a precise count | `502000` |
| `viewCountText` | Original displayed view label | `502K` |
| `fromYTUrl` | Channel tab used to find the item | `https://www.youtube.com/@NASA/videos` |
| `type` | Record kind | `video` |
| `thumbnailUrl` | Public thumbnail address | `https://i.ytimg.com/vi/IwZVXmQdX1E/hq720.jpg` |
| `order` | Zero-based position within a channel and type | `0` |
| `videoDescription` | Public video description when details are enabled | `No aim is too high...` |
| `videoDescriptionLinks` | URLs linked from that description | `[]` |
| `channelId` | Stable public channel ID | `UCLA_DiR1FfKNvjuUpBHmylQ` |
| `channelName` | Displayed channel name | `NASA` |
| `channelUsername` | Handle without the at sign | `NASA` |
| `channelUrl` | Canonical channel URL | `https://www.youtube.com/channel/UCLA_DiR1FfKNvjuUpBHmylQ` |
| `channelDescription` | Public channel biography | `NASA's mission is to pioneer...` |
| `channelDescriptionLinks` | Public links in the channel profile | `https://www.nasa.gov` |
| `channelJoinedDate` | Join date as displayed | `Jun 3, 2008` |
| `channelLocation` | Public location if present | `null` |
| `channelAvatarUrl` | Public avatar address | `https://yt3.googleusercontent.com/...` |
| `channelBannerUrl` | Public banner address | `https://yt3.googleusercontent.com/...` |
| `channelTotalVideos` | Total count shown on the About page | `6175` |
| `channelTotalViews` | Total channel views shown on the About page | `1123352904` |
| `numberOfSubscribers` | Numeric approximation of the subscriber label | `15100000` |
| `subscriberCountText` | Original subscriber label | `15.1M subscribers` |
| `isChannelVerified` | Whether a verified badge appears in the header | `true` |
| `isAgeRestricted` | Age restriction status when it can be determined | `null` |
| `input` | Normalized channel URL supplied for the row | `https://www.youtube.com/@NASA` |
| `error` | Code on an error row only | `CHANNEL_NOT_FOUND` |
| `note` | Short explanation on an error row only | `Channel metadata was unavailable` |

`thumbnailUrl` values may include image sizing parameters and can change even when the underlying video stays the same. Use `id` and `channelId` as durable identifiers. The `channelTotalVideos` value counts videos shown in the public channel profile; it is not the number of rows requested for this run. Subscriber counts are frequently rounded by the site, so `15100000` represents the displayed `15.1M`, not a precise private count.

### How to scrape YouTube channels

1. Add one or more public channel URLs to **Channel URLs**. Handles such as `https://www.youtube.com/@NASA`, `/channel/` URLs, and legacy `/c/` and `/user/` paths are accepted.
2. Choose limits for regular videos, Shorts, and streams. A limit applies separately to every channel. Keep unused types at zero. For a profile-only export, set all three limits to zero.
3. Select Newest, Popular, or Oldest. If you need descriptions, linked URLs, and exact publication dates, turn on **Include video details**.
4. Start the actor and open its default dataset when it finishes. Download JSON, CSV, or Excel, or read the dataset through the API.

For a recurring report, save the same input and run it on a schedule. Compare rows by `channelId` and `id`. A video can move between lists as a channel changes its layout or popularity ranking, so the visible position is a snapshot, not an identifier.

### Input

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `startUrls` | URL list | Required | Public channel URLs. Multiple channels can be entered in one run. |
| `maxResults` | Integer | `10` | Maximum regular videos per channel; `0` skips videos. |
| `maxResultsShorts` | Integer | `0` | Maximum Shorts per channel; `0` skips Shorts. |
| `maxResultStreams` | Integer | `0` | Maximum streams per channel; `0` skips streams. |
| `sortVideosBy` | String | `NEWEST` | `NEWEST`, `POPULAR`, or `OLDEST` on each requested tab. |
| `oldestPostDate` | String | Empty | Earliest date for regular videos and streams, as `YYYY-MM-DD` or a relative age such as `30 days`. |
| `includeVideoDetails` | Boolean | `false` | Read each result's public watch page for description, links, exact date, and a more precise view count. |
| `maxItems` | Integer | `0` | Total output cap across all channels; `0` has no global cap. |
| `proxyConfiguration` | Object | Public proxy | Proxy configuration for channel and video pages. |

A realistic input for a first run is:

```json
{
  "startUrls": [{"url": "https://www.youtube.com/@NASA"}],
  "maxResults": 35,
  "maxResultsShorts": 0,
  "maxResultStreams": 0,
  "sortVideosBy": "NEWEST",
  "includeVideoDetails": false
}
```

The three per-type limits are requested independently for each channel. With two channels and limits of 10 videos and 5 Shorts, the run may return up to 30 rows if both channels have enough public items. `maxItems` can stop the entire run earlier. When every per-type limit is zero, the actor returns one row with `type: "channel"` for each valid channel. That row contains the channel profile fields and the channel URL.

### Output example

This item came from a successful 35-video run on September 27, 2026. Long description and image URLs are shortened here; the dataset contains the complete values.

```json
{
  "id": "IwZVXmQdX1E",
  "title": "NASA Moon Base: The First Six Months",
  "duration": "1:07",
  "date": "2w ago",
  "publishedAt": null,
  "url": "https://www.youtube.com/watch?v=IwZVXmQdX1E",
  "viewCount": 502000,
  "viewCountText": "502K",
  "fromYTUrl": "https://www.youtube.com/@NASA/videos",
  "type": "video",
  "order": 0,
  "channelId": "UCLA_DiR1FfKNvjuUpBHmylQ",
  "channelName": "NASA",
  "channelUsername": "NASA",
  "channelUrl": "https://www.youtube.com/channel/UCLA_DiR1FfKNvjuUpBHmylQ",
  "channelJoinedDate": "Jun 3, 2008",
  "channelTotalVideos": 6175,
  "channelTotalViews": 1123352904,
  "numberOfSubscribers": 15100000,
  "subscriberCountText": "15.1M subscribers",
  "isChannelVerified": true,
  "input": "https://www.youtube.com/@NASA"
}
```

The default dataset is a flat table. Profile details repeat on video rows to make each exported record self-contained. If an input URL cannot be resolved, an error row identifies that input instead of silently dropping it. Normal rows have no `error` field. Check for that field before treating every row as a video.

### Use cases

- **Channel monitoring:** record new uploads, Shorts, and streams on a schedule, then compare video IDs and publication dates between runs.
- **Content research:** compare titles, durations, and view labels within the same channel or across several public channels.
- **Campaign reporting:** attach public channel and video URLs to a campaign report and track view-count changes over time.
- **Publisher catalogs:** build a list of public videos from a collection of channel handles, with stable IDs and thumbnail references.
- **Audience research:** compare public subscriber labels, channel descriptions, and external profile links across a selected set of channels.
- **Editorial planning:** inspect Popular and Oldest lists beside recent uploads to understand which public formats remain visible over time.

The output describes public pages at the moment of collection. A view count can rise, a title can change, and a video can be removed. For repeatable analysis, retain the run date alongside exported data and join records by their IDs.

### How much does it cost to scrape YouTube channels?

The configured price is **$0.001 per delivered dataset item, or $1.00 per 1,000 results**. The event charge applies to each output row, including a profile-only row or an error row. Platform usage is included in the event price shown for this actor. The configured price should be checked on the pricing tab before a large production run because it can change.

For example, 100 rows cost $0.10 in result charges. A 1,000-row export costs $1.00, and 5,000 rows cost $5.00. If a global limit stops a run at 250 rows, the result charge is $0.25 even when the sum of per-channel limits is higher. A profile-only run with 25 valid channels produces 25 rows and costs $0.025 in result charges.

**Include video details** does not add a separate per-row event charge, but it requests one additional public page for each selected video. It can increase runtime. The fast list-only mode is a good starting point for broad channel inventories; request details when exact dates or description links matter to the analysis.

### Tips for best results

Start with a small limit on one channel and inspect the fields you intend to use. Increase `maxResults` after checking whether the channel has enough public uploads. Set the unused type limits to zero so the run does not fetch tabs you do not need. The default input collects regular videos only, which keeps the initial result focused.

Use a channel URL or handle rather than a watch URL. A watch URL identifies one video, while this actor starts from a channel profile. `/channel/` URLs are useful when a channel handle changes because the channel ID is stable. You can mix handle and ID URLs in one input; duplicate normalized URLs are skipped within a run.

For current uploads, leave sorting at `NEWEST`. For a historical catalog, `OLDEST` can surface early uploads; `POPULAR` displays the site's ranking at collection time. Sorting is read from the public tab controls, so a channel without a requested control may fall back to its visible list. A date filter works on regular videos and streams. It is most efficient with `NEWEST`, where collection can stop once older items are reached.

Turn on `includeVideoDetails` when the precise publication date or public description links are needed. The list view often shows labels such as `2w ago` and rounded counts such as `502K`; the additional page can provide `2026-09-08` and a more precise count. The channel profile itself is collected separately from the video lists, so all video rows carry the same channel context.

### Limits

Only publicly visible channel and video data is collected. Private, deleted, login-only, or region-restricted material may be absent. Some public pages can show a sign-in prompt in the player response; the channel list and, when present, structured watch-page data can still expose public metadata. If a needed field is not present in those public records, it remains `null`.

YouTube may round subscriber and list-page view counts. The numeric fields convert the displayed abbreviations for sorting and filtering, but they should not be treated as exact measurements. `subscriberCountText` and `viewCountText` preserve the source labels. The About page may omit a channel location; `channelLocation` is then `null`. Age restriction is not reliably indicated by a standard public channel field, so `isAgeRestricted` can also be `null`.

Shorts list entries may omit dates and durations. The `oldestPostDate` filter does not apply to Shorts. On the channel examined during development, the initial Shorts list contained 40 entries and did not provide a next-page token, so requesting more may still return only the available initial batch. Regular video and stream lists supplied browse continuation tokens and were paged in the verified runs. Other channels or future site layouts can differ.

The date filter uses the public list label where possible and an exact date from a watch page when needed. Relative labels have limited precision near a cutoff. For strict day-level reporting, enable video details and inspect `publishedAt`. A Popular or Oldest list is ranked by the site; it is not a full archive guarantee, and output may end when the site stops providing continuation data.

### Use with MCP

The input and output schemas expose the channel URL list, per-type limits, sort order, and optional details. An MCP client can run the actor and read its dataset without manual export. Two useful prompts are:

> Collect the 50 newest public videos from this channel URL and return their titles, URLs, publication labels, and view counts.

> Compare the 20 most popular videos and 20 most popular Shorts from these two channel URLs. Keep the channel ID and video ID in each row.

For follow-up requests, specify whether approximate list counts are acceptable or whether exact video dates and public description links are required. The latter requires `includeVideoDetails: true` and takes longer.

### FAQ

#### Is scraping YouTube channels legal?

The actor reads public pages. Whether a particular use is permitted depends on applicable law, site terms, and the way the data will be used. Respect privacy and intellectual-property rights. Do not use public profile or video information to infer private attributes or misuse personal data. Obtain appropriate advice for regulated or high-impact uses.

#### Do I need a data API key or a proxy?

No data API key is needed. The actor uses public channel pages and their browse responses. A proxy is configured by default for reliable access from the platform. The fetch layer starts with a standard proxy and can escalate after repeated blocks. Network conditions and site controls can change, so no access method is guaranteed for every channel.

#### How fast is a run?

Runtime depends on the number of channels, selected types, continuation pages, and whether video details are enabled. A verified 35-video list-only run completed successfully in roughly ten seconds of remote run time. A detail-enabled run reads a public watch page for every selected video and takes longer. Large exports and transient network retries can add time.

#### Can I schedule or monitor repeated runs?

Yes. Save an input configuration and run it on a schedule. Compare `id` and `channelId` against prior datasets to find new items. Use the run status and dataset item count for monitoring. A successful run can still return fewer rows than requested when a channel has fewer public items or a tab stops supplying more entries.

#### Can I export to spreadsheets?

Yes. The default dataset can be exported as CSV or Excel, and its flat channel fields make spreadsheet filtering straightforward. Keep URL and ID columns as text to prevent unwanted formatting. For recurring sheets, append a collection timestamp outside the actor output so snapshots remain distinguishable.

#### Why is a field empty?

The site may not display it on that page, the content type may not have the field, or the field may require **Include video details**. Shorts commonly lack list-page dates and durations. Location is often absent from a channel profile. A missing `publishedAt` in fast mode is expected; the list's `date` label remains available for regular videos and streams.

#### How do I get only the channel profile?

Set `maxResults`, `maxResultsShorts`, and `maxResultStreams` to `0`. Each valid channel then produces one row with `type: "channel"`, its canonical URL, description, public links, avatar, banner, counts, and other available profile fields. This avoids fetching video tabs when only channel-level information is needed.

### Integrations

Use the platform API to start runs and read the default dataset, or connect a completed run to a webhook. Scheduled runs can feed a reporting pipeline through CSV or JSON exports. Integration services can route new dataset rows into a spreadsheet, database, or notification workflow. Keep the stable channel and video IDs in those downstream records so repeated collections can update existing rows cleanly.

### Support

Open an issue on the Issues tab; we reply within 24h and add fields on request.

# Actor input Schema

## `startUrls` (type: `array`):

Public channel URLs such as https://www.youtube.com/@NASA. Handles, channel IDs, legacy custom and user URLs are accepted.

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

Maximum regular videos to collect from each channel. Set to 0 to skip videos.

## `maxResultsShorts` (type: `integer`):

Maximum Shorts to collect from each channel. Set to 0 to skip Shorts.

## `maxResultStreams` (type: `integer`):

Maximum streams to collect from each channel. Set to 0 to skip streams.

## `sortVideosBy` (type: `string`):

Sort each requested channel tab by newest, popular, or oldest.

## `oldestPostDate` (type: `string`):

Optional earliest date for videos and streams as YYYY-MM-DD or a relative age such as 30 days. Shorts lack a list-page date and are not filtered.

## `includeVideoDetails` (type: `boolean`):

Read each video page for description links, an exact publication date, and a more precise view count. This makes runs slower.

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

Stop after this many items across all channels. Set to 0 for no global cap.

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

Proxy settings for public pages. The actor starts with the standard proxy and escalates only if blocked.

## Actor input object example

```json
{
  "startUrls": [
    {
      "url": "https://www.youtube.com/@NASA"
    }
  ],
  "maxResults": 35,
  "maxResultsShorts": 0,
  "maxResultStreams": 0,
  "sortVideosBy": "NEWEST",
  "oldestPostDate": "",
  "includeVideoDetails": false,
  "maxItems": 0,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All channel and video records in the default dataset.

# 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 = {
    "startUrls": [
        {
            "url": "https://www.youtube.com/@NASA"
        }
    ],
    "maxResults": 35,
    "maxResultsShorts": 0,
    "maxResultStreams": 0
};

// Run the Actor and wait for it to finish
const run = await client.actor("mlg14/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 = {
    "startUrls": [{ "url": "https://www.youtube.com/@NASA" }],
    "maxResults": 35,
    "maxResultsShorts": 0,
    "maxResultStreams": 0,
}

# Run the Actor and wait for it to finish
run = client.actor("mlg14/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 '{
  "startUrls": [
    {
      "url": "https://www.youtube.com/@NASA"
    }
  ],
  "maxResults": 35,
  "maxResultsShorts": 0,
  "maxResultStreams": 0
}' |
apify call mlg14/youtube-channel-scraper --silent --output-dataset

```

## MCP server setup

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