# YouTube Video Scraper · Whole Channels, Exact Views & Likes (`thequietstack/youtube-channel-videos`) Actor

YouTube video scraper for one or many channels: every video with exact views and likes (not "690K"), publish date, comments, duration and HD thumbnail. Date window, title filter, min views and only-new mode. No login, no API key, no quota.

- **URL**: https://apify.com/thequietstack/youtube-channel-videos.md
- **Developed by:** [TheQuietStack](https://apify.com/thequietstack) (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 $2.00 / 1,000 videos

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 Video Scraper · Whole Channels, Exact Views & Likes

**YouTube video scraper for whole channels:** every video of one or many channels with exact views and likes, publish date and duration, without an API key.

Give it YouTube channels, get **every video as one flat row** with the **exact view count** (`692,960`, not `690K`), the **exact like count**, the publish date, comments, duration, live status, HD thumbnail, hashtags and the full description. Filter by date window, title words and minimum views; schedule it with **only-new mode** to track a channel's uploads.

No login, no Google account, no API key, no YouTube Data API quota.

### What you get

| | This Actor | The channel page / most channel scrapers |
|---|---|---|
| Views | `692960` (exact) | `690K views` (truncated) |
| Likes | `12916` (exact) | not shown in the list |
| Publish date | `2026-09-30` | `18 hours ago` |
| Thumbnail | 1280x720 `maxresdefault` + the largest listed one | 360px |
| Title filter | before the video is opened, never charged | - |

### Use cases

- **Competitor and creator research:** pull the last 100 uploads of 20 channels and sort by real views or likes per view - the order is often different from what "1.2M views" suggests.
- **Influencer vetting:** like rate (`likeRatePct`), upload rhythm and view spread over a date window before you sign a sponsorship.
- **Content monitoring:** schedule a daily run with `onlyNewVideos` and get only the uploads since yesterday into Google Sheets, Make, n8n or Zapier.
- **Topic tracking:** `titleKeywords: ["review"]` over a list of tech channels returns only review videos, with numbers.
- **Datasets for analysis or AI:** clean JSON with descriptions and hashtags, one row per video.

### Input example

```json
{
    "channels": ["@veritasium", "https://www.youtube.com/@NASA"],
    "daysBack": 60,
    "maxVideosPerChannel": 50,
    "titleKeywords": [],
    "minViews": 10000,
    "includeChannelProfile": true
}
```

- `channels` - `@handle`, `handle`, `youtube.com/@handle`, `/channel/UC...`, `/c/...`, `/user/...` or a bare channel ID. Video links are rejected with a clear message.
- `tab` - `videos` (uploads, default) or `streams` (the Live tab). Shorts have their own Actor: [YouTube Shorts Analytics](https://apify.com/thequietstack/youtube-shorts-analytics).
- `dateFrom` / `dateTo` (YYYY-MM-DD, inclusive) or `daysBack`.
- `titleKeywords` + `keywordMode` (`any` / `all`), `excludeTitleKeywords`, `minViews`.
- `maxVideosPerChannel`, `maxVideos` (whole run), `maxPagesPerChannel` (30 videos per list page).
- `includeChannelProfile`, `includeDescription`, `onlyNewVideos` + `stateStoreName`, `requestDelaySecs`.

### Output example

Real rows from a run on 01 Oct 2026 (description shortened):

```json
{
    "recordType": "video",
    "videoId": "JsBZOcqZerk",
    "url": "https://www.youtube.com/watch?v=JsBZOcqZerk",
    "title": "The Insane Real Engineering of the Nazi Enigma Machine",
    "channelTitle": "Veritasium",
    "channelHandle": "@veritasium",
    "channelId": "UCHnyfMqiRRG1u-2MsSQLbXA",
    "contentType": "video",
    "publishedDate": "2026-09-21",
    "publishedDateText": "Sep 21, 2026",
    "publishedDateApprox": false,
    "listedAgeText": "9 days ago",
    "views": 14227041,
    "viewsText": "14,227,041 views",
    "viewsExact": true,
    "listedViewsText": "14M views",
    "likes": 115226,
    "likesText": "115,226",
    "likesExact": true,
    "likeRatePct": 0.81,
    "comments": 6000,
    "commentsText": "6K",
    "commentsExact": false,
    "isLiveNow": false,
    "liveViewers": null,
    "durationSeconds": 2862,
    "durationText": "47:42",
    "badges": [],
    "thumbnailUrl": "https://i.ytimg.com/vi/JsBZOcqZerk/hq720_custom_2.jpg?sqp=...",
    "thumbnailMaxresUrl": "https://i.ytimg.com/vi/JsBZOcqZerk/maxresdefault.jpg",
    "hashtags": [],
    "description": "How was the \"unbreakable\" enigma cracked? ...",
    "scrapedAt": "2026-10-01T14:28:10.705Z"
}
```

More real rows from the same run:

| Channel | Video | Published | Views | Likes |
|---|---|---|---|---|
| @veritasium | We're trying something new... | 2026-09-30 | 692,960 | 12,916 |
| @veritasium | We sent a camera to space to film the solar eclipse | 2026-08-17 | 2,713,646 | 67,412 |
| @veritasium | Is spider web really stronger than steel? | 2026-08-02 | 9,373,637 | 141,990 |
| @NASA | NASA Moon Base: The First Six Months | 2026-09-08 | 509,890 | 8,859 |
| @NASA | For Earth, With Love: Artemis II | 2026-09-03 | 434,439 | 9,616 |

With `includeChannelProfile`, one row per channel has `recordType: "channel"`: `channelId`, `handle`, `title`, `description`, `keywords`, `subscribers` + `subscribersText` + `subscribersExact`, `videoCount` (+ text, exact flag), `avatarUrl`, `verified`, `isFamilySafe`, `rssUrl`, `url`.

The run summary (`SUMMARY` in the key-value store) lists per channel: status (`ok`, `partial`, `not_found`, `not_a_channel`, `no_such_tab`, `failed`, `blocked`), videos written, list pages, watch-data requests, everything skipped (after `dateTo`, title, `minViews`, upcoming, unavailable, already seen, duplicates) and why paging stopped.

### Works with: YouTube Comments Monitor

Monitoring a channel end to end? Pair this Actor with [YouTube Comments Monitor](https://apify.com/thequietstack/youtube-comments-monitor): it takes the same channel handles (or the video URLs from this dataset) and returns the comments and replies, with `onlyNewComments` so a daily schedule only pays for new comments. Videos here, audience reaction there.

### How much does it cost?

Pay per result, nothing else: **$0.002 per video** ($2.00 per 1,000) and **$0.002 per channel profile**. You are charged per video row written to the dataset and per channel profile row; the platform usage is included.

| Job | Rows | Cost |
|---|---|---|
| Last 50 videos of one channel | 50 | $0.10 (+ $0.002 profile) |
| 20 competitor channels x 50 videos | 1,000 | $2.00 (+ $0.04 profiles) |
| Daily monitoring of 10 channels, `onlyNewVideos`, ~1 upload per channel per day | ~300 / month | ~$0.60 per month |

Never charged: unknown or invalid channels, a missing Live tab, blocked or failed requests, videos outside the date window, title or `minViews` misses, upcoming premieres, deleted videos, duplicates and videos already delivered by an earlier `onlyNewVideos` run. Set *Max cost per run* in Apify and the run stops cleanly at that amount.

### Honest numbers

- **Views and likes are exact** when YouTube shows them exactly (it does on the watch data for regular videos). If a video only shows a rounded value, you get it with `viewsExact: false` / `likesExact: false` - a rounded number is never presented as exact. Hidden like counts stay `null`.
- **Comment counts are rounded by YouTube** (`1.8K`). You get the number, the text and `commentsExact: false`. Disabled comments give `null`.
- **Subscriber counts are rounded by YouTube** (`21.3M`); the profile row flags them with `subscribersExact: false`.
- **Publish date is a calendar day** as YouTube shows it (`Sep 30, 2026`). For streams that are live or ended a few hours ago YouTube only says "60 minutes ago"; then the day is computed and `publishedDateApprox: true`.
- **Live now:** a running stream has no view count yet. `views` is `null` and `liveViewers` holds the concurrent viewers.
- `thumbnailMaxresUrl` follows YouTube's fixed pattern; it exists for HD uploads, very old or low-resolution uploads may only have the listed `thumbnailUrl`.
- Measured on 01 Oct 2026 against YouTube's own channel RSS feed: same videos, same dates, view and like counts within the minutes between the two reads.

### Polite access - Bot protection is never bypassed

- Only YouTube's public web-client data for channel lists and the watch page, the same requests youtube.com makes for a visitor who is not logged in. The video player / stream endpoint is never called, nothing is downloaded.
- One request at a time with a pause (`requestDelaySecs`, minimum 0.5 s, default 1 s), hard caps per channel and per run, and filters applied before a video is opened.
- **Bot protection is never bypassed.** If YouTube answers with HTTP 403 or 429, a captcha or "confirm you're not a bot" page, a consent or login redirect, or an answer without the expected structure, the Actor stops the whole run at once with status FAILED and a clear message (`SUMMARY.blocked` names the channel and the reason; remaining channels are listed under `notReached`). No retries of a block, no proxy or IP rotation, no header tricks, no login, no cookies, no consent clicking. Rows written before the stop stay in the dataset; nothing is charged for what was not delivered.
- A plain server error (5xx) is retried exactly once after 5 seconds; anything else is reported, not retried.

### FAQ

**Do I need a YouTube API key?** No. And there is no daily quota like the YouTube Data API has.

**Does it get transcripts?** No. YouTube now requires a client attestation token for captions; getting it would mean working around bot protection, which this Actor never does.

**Shorts?** Use the sister Actor [YouTube Shorts Analytics](https://apify.com/thequietstack/youtube-shorts-analytics) - same exact-number approach for the Shorts tab.

**How far back can it go?** The full Videos tab of a channel, 30 videos per list page, capped by `maxPagesPerChannel` (default 40 pages = 1,200 videos). With `dateFrom`, paging stops at the first older video.

**Why is a video missing that I see on the channel?** Check `SUMMARY`: it was filtered (date, title, minViews), is an upcoming premiere, was deleted between listing and opening, or the channel hit a limit. Each case is counted.

**Is it legal?** It reads public pages without logging in. You are responsible for how you use the data; respect YouTube's Terms of Service and the rights of creators when you reuse titles, descriptions or thumbnails. Channel data can contain personal data (names, faces in thumbnails); you need your own legal basis (e.g. GDPR) for your purpose. This Actor is not affiliated with or endorsed by YouTube or Google.

### Use it via API, integrations and AI agents

```bash
curl -X POST "https://api.apify.com/v2/acts/thequietstack~youtube-channel-videos/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"channels":["@veritasium"],"daysBack":30,"maxVideosPerChannel":50}'
```

```python
from apify_client import ApifyClient

client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("thequietstack/youtube-channel-videos").call(
    run_input={"channels": ["@veritasium", "@NASA"], "daysBack": 30, "minViews": 10000}
)
for video in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(video["publishedDate"], video["views"], video["likes"], video["title"])
```

Works with Apify schedules, webhooks and integrations (Google Sheets, Make, n8n, Zapier), and as a tool for AI agents through the Apify MCP server.

# Actor input Schema

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

Channel handles or links: @veritasium, veritasium, https://www.youtube.com/@NASA, youtube.com/channel/UC..., /c/... or /user/... links, or a bare channel ID (UC...). Video links are rejected.

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

The channel's Videos tab or its Live tab. Shorts have their own Actor: thequietstack/youtube-shorts-analytics.

## `dateFrom` (type: `string`):

YYYY-MM-DD. Paging stops at the first video published before this day.

## `dateTo` (type: `string`):

YYYY-MM-DD. Newer videos are skipped without opening them and are not charged.

## `daysBack` (type: `integer`):

Shortcut for dateFrom = today minus N days. Ignored when dateFrom is set.

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

Stop each channel after this many written videos.

## `maxVideos` (type: `integer`):

The run stops cleanly at this number. You are never charged for more videos than this.

## `titleKeywords` (type: `array`):

Keep only videos whose title contains these words (case-insensitive). Checked before a video is opened; misses are not charged.

## `keywordMode` (type: `string`):

Whether a title needs one or all of the words.

## `excludeTitleKeywords` (type: `array`):

Drop videos whose title contains any of these words, e.g. #shorts, trailer, live. Not charged.

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

Keep only videos with at least this many views (checked on the exact count). Videos that clearly miss it are skipped from the list without being opened. Not charged.

## `includeDescription` (type: `boolean`):

The full video description (and hashtags from it). Switch off for smaller datasets.

## `includeChannelProfile` (type: `boolean`):

One extra row per channel (recordType "channel"): title, handle, description, subscribers as shown (flagged as rounded), video count, avatar, verified flag, RSS URL. Charged as one channel-profile event.

## `onlyNewVideos` (type: `boolean`):

For scheduled monitoring: remembers the videos already seen per channel in a named key-value store and stops at the first known one on the next run. You are not charged again for videos you already have.

## `stateStoreName` (type: `string`):

Named key-value store for the only-new-videos memory. Use a different name per monitoring task.

## `maxPagesPerChannel` (type: `integer`):

Safety cap. One list page holds 30 videos.

## `requestDelaySecs` (type: `number`):

Requests go out one at a time with this pause. Lower is not allowed: this Actor crawls politely.

## Actor input object example

```json
{
  "channels": [
    "@veritasium",
    "https://www.youtube.com/@NASA"
  ],
  "tab": "videos",
  "maxVideosPerChannel": 50,
  "maxVideos": 500,
  "titleKeywords": [
    "review",
    "tutorial"
  ],
  "keywordMode": "any",
  "excludeTitleKeywords": [
    "trailer"
  ],
  "includeDescription": true,
  "includeChannelProfile": false,
  "onlyNewVideos": false,
  "stateStoreName": "youtube-channel-videos-state",
  "maxPagesPerChannel": 40,
  "requestDelaySecs": 1
}
```

# Actor output Schema

## `videos` (type: `string`):

One row per video: video ID, URL, title, channel, publish date, exact views, likes (with exact flag), comments (with exact flag), duration, live status, thumbnails, hashtags, description. Channel profile rows carry recordType "channel".

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

Per channel: status (ok, partial, not\_found, not\_a\_channel, no\_such\_tab, failed, blocked), videos written, list pages, watch-data requests, what was skipped and why paging stopped. Nothing listed as skipped is charged.

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

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

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

```

## MCP server setup

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

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/Tvx1Cet0ZcVDuyY2m/builds/KbOv5SDVjwxz9anA3/openapi.json
