# YouTube Scraper (search, channels, Shorts, playlists, stats) (`scrapemole/youtube-video-channel-scraper`) Actor

Scrape YouTube search results, channel videos, Shorts and live streams, and playlists: title, views, publish date, duration, channel and subscribers. Optional full details: likes, comments count, description, category, tags. No login, no API key, no empty rows. Pay only per video delivered.

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

## Pricing

from $0.60 / 1,000 video (list row)s

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Scraper: videos, channels, Shorts and playlists

Scrape **YouTube search results, channel videos, Shorts, live streams and playlists** without an API key or quota. Every row has the title, link, channel, **views**, upload time, **duration** and thumbnail. Channel rows also carry the **subscriber count**. Turn on **full details** to add **likes, comment count, exact publish date, category, description and tags**.

**You only pay for videos delivered.** Channels, playlists or videos that don't exist, invalid links and rate-limited requests are reported as clear error rows and never charged.

### Why this YouTube scraper doesn't stall or come back empty

- **Reads the same JSON the YouTube website loads** (no headless browser), so it is fast: a 100-result search takes about 4 seconds, and 150 playlist videos take under 2 seconds.
- **Pages correctly.** It follows the continuation that belongs to the list you asked for, not to sidebars or comment panels, which is the usual reason scrapers stop at 20 or 30 results.
- **Filters you can trust.** Search filters (upload date, length, sort) are applied by YouTube and then *checked again* against each result, and shelves of older "related" videos are skipped. In our live tests, 281 of 281 filtered results respected the filter.
- **Retries with backoff and a fresh IP** on rate limits, switches to residential IPs if needed, and makes a second, slower pass for anything still rate-limited.
- **Clear errors**: `NOT_FOUND` (channel, playlist or video doesn't exist or is private), `NO_DATA` (for example, a channel with no Shorts tab), `INVALID_INPUT` (not a YouTube link), `BLOCKED` (rate-limited after all retries).

### What you get

| Field | Description |
|---|---|
| `videoId`, `url`, `title` | The video (Shorts get a `/shorts/` link) |
| `channelName`, `channelHandle`, `channelId`, `channelUrl` | Who published it |
| `channelSubscribers`, `channelVideoCountText`, `channelDescription` | Channel sources only |
| `viewCount`, `viewCountText` | Views as a number and as shown |
| `publishedText`, `publishedAtApprox` | "3 weeks ago" as shown, and the date it implies |
| `durationSeconds`, `durationText`, `isShort`, `isLive`, `badges` | Length and format |
| `position`, `sourceType`, `searchQuery`, `playlistTitle`, `playlistVideoCount`, `channelTab` | Where the video was found |
| `likeCount`, `commentCount`, `publishDate`, `uploadDate`, `category`, `description`, `keywords` | **Full details** mode |
| `descriptionSnippet`, `thumbnailUrl` | Snippet (search results) and best thumbnail |

E-mail addresses in descriptions are replaced with `[email removed]`: we don't collect personal data.

### How to scrape YouTube

1. Add **search keywords**, **channels** (`@handle`, channel URL or ID), **playlists** or single **videos**. You can mix them, or paste any YouTube links into *Any YouTube URLs*.
2. Set **Max videos per search, playlist or channel tab** (default 50).
3. Optional: search **Shorts only** (Search type: Shorts), sort and filter searches by upload date or length, choose channel tabs (Videos, Shorts, Live), keep only videos **newer than N days**, or turn on **Full video details**.
4. Click **Start** and download JSON, CSV or Excel, or use the API.

#### Input example

```json
{
  "searchQueries": ["espresso machine review"],
  "channels": ["@mkbhd"],
  "channelContent": ["videos", "shorts"],
  "playlists": ["https://www.youtube.com/playlist?list=PLRqwX-V7Uu6ZiZxtDDRCi6uhfTH4FilpH"],
  "maxResultsPerSource": 100,
  "newerThanDays": 30
}
```

#### Output example (search, full details)

```json
{
  "status": "ok",
  "sourceType": "search",
  "searchQuery": "espresso machine review",
  "position": 1,
  "videoId": "mAYbmcqPphc",
  "url": "https://www.youtube.com/watch?v=mAYbmcqPphc",
  "title": "The Fellow ES1 Espresso Machine Review",
  "channelName": "James Hoffmann",
  "channelHandle": "@jameshoffmann",
  "viewCount": 804647,
  "likeCount": 13733,
  "commentCount": 1000,
  "publishDate": "2026-08-13T08:23:20-07:00",
  "durationSeconds": 2056,
  "category": "Entertainment",
  "isShort": false
}
```

#### Monitoring channels for new uploads

Add the channels, set **Only videos newer than (days)** to 1 or 7 and schedule the Actor daily. Channel tabs are newest first, so the run stops at the first older video: you pay only for new uploads.

### Switching from another YouTube Actor

Already calling **streamers/youtube-scraper** from code, a schedule or Make/Zapier? Change the Actor ID and keep your input JSON: this Actor accepts the same field names.

| Their input | Here |
|---|---|
| `searchQueries`, `startUrls` | Same |
| `maxResults` / `maxResultsShorts` / `maxResultStreams` | Same: sets results per source and which channel tabs are read (videos, shorts, streams); Shorts-only means a Shorts search |
| `sortingOrder` (`relevance`, `rating`, `date`, `views`) | Same values (`sortBy`) |
| `dateFilter` (`hour`, `today`, `week`, `month`, `year`) | Same values (`uploadDate`) |
| `lengthFilter` (`under4`, `between420`, `plus20`) | Same values (`duration`) |
| `oldestPostDate` (`2026-01-01` or `7 days`) | Same (`newerThanDays`) |
| Transcripts, subtitles, AI summaries, the feature-flag booleans | Ignored; use the ScrapeMole YouTube Transcript Scraper for transcripts |

### Pricing

Pay per event, no start fee:

| Event | Free | Starter (Bronze) | Scale (Silver) | Business (Gold) |
|---|---|---|---|---|
| `video`: video from a search, channel or playlist | $0.0015 | $0.001 | $0.0008 | $0.0006 |
| `video-details`: video with full details | $0.003 | $0.002 | $0.0016 | $0.0012 |

1,000 videos from searches cost $1.00 on the Starter plan, about a quarter of the most-used YouTube scraper. Errors and duplicates are free. Set a maximum cost per run and the Actor stops cleanly at that budget.

### Use cases

- **Market and competitor research:** what ranks for your keywords, who publishes it, how it performs.
- **Influencer discovery:** channels in a niche, with subscriber counts and recent views.
- **Content monitoring:** new uploads from a list of channels every day.
- **Datasets for AI:** titles, descriptions, tags and engagement for training or RAG. Pair with the ScrapeMole YouTube Transcript Scraper for the spoken text.

### API, Python and AI agents

```python
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("scrapemole/youtube-video-channel-scraper").call(run_input={"searchQueries": ["espresso machine review"], "maxResultsPerSource": 100})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["title"], item.get("viewCount"))
```

Works with schedules, webhooks, Make, Zapier, n8n and Apify's MCP server for AI agents.

### Test results

Live tests on 8 Oct 2026 from a single server IP **without any proxy**: 10 searches with mixed sort, date and length filters, plus channels and videos *discovered by those searches* (not hand-picked) and 5 public playlists.

| Measure | Result |
|---|---|
| Runs finished OK | **40/40** (2 sessions of 20) |
| Searches, channels and playlists returning videos | **40/40** |
| Videos delivered from lists | 2,321 |
| Full-details lookups | **144/144** |
| Results respecting the requested filters | **694/694** |
| Likes / comment count found (details) | 72/72 / 71/72 (latest session) |
| Billing matched delivered results | Yes, every run |
| Not-found channel, playlist and video + invalid URL | All reported, nothing charged |
| Budget cap (7 of 50) | Stopped at exactly 7 |

### FAQ

**Do I need a YouTube API key?** No key and no quota.

**How many results can a search return?** YouTube itself usually stops at around 500–600 results per query. Split big topics into several keywords.

**Why is `publishedText` null for some rows?** YouTube doesn't show an upload time for Shorts in channel lists. Use full details for exact dates.

**Is this legal?** The Actor collects publicly available video data and no personal data. You are responsible for how you use the data; check YouTube's terms and the laws that apply to you.

### More from ScrapeMole

ScrapeMole builds focused, reliable scrapers for public data: pay per result, no start fees, clear error rows that are never charged, and a `RUN_SUMMARY` on every run.

- [YouTube Transcript & Subtitles Scraper](https://apify.com/scrapemole/youtube-transcript-scraper)
- [Google Trends Scraper](https://apify.com/scrapemole/google-trends-scraper)
- [YouTube Comments Scraper](https://apify.com/scrapemole/youtube-comments-scraper)
- [Telegram Channel Scraper](https://apify.com/scrapemole/telegram-channel-scraper)
- [Amazon Product & Search Scraper](https://apify.com/scrapemole/amazon-product-scraper)

Building an AI agent with its own wallet? The YouTube and Google Trends data is also sold per call over [x402](https://www.x402.org) (USDC on Base, no account): see [scrapemole.pages.dev](https://scrapemole.pages.dev). Questions or a missing field? Open an issue on this Actor. ScrapeMole is the maker of this Actor.

# Actor input Schema

## `searchQueries` (type: `array`):

Search YouTube exactly like the search box. Returns videos in result order (position 1, 2, 3...). Use the filters below for sort order, upload date and length.

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

@handles (e.g. @mkbhd), channel URLs (youtube.com/@name, /channel/UC..., /c/..., /user/...) or channel IDs. Returns the channel's videos newest first, with channel name and subscriber count on every row.

## `channelContent` (type: `array`):

Which channel tabs to scrape. The result limit applies to each tab separately.

## `playlists` (type: `array`):

Playlist URLs (youtube.com/playlist?list=PL...) or playlist IDs. Returns videos in playlist order with the playlist title and size.

## `videos` (type: `array`):

Video URLs or IDs (watch, youtu.be, Shorts, embed, live). Always returns full details: views, likes, comments count, publish date, category, description and tags.

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

Mixed list of channel, playlist and video links. Each is detected automatically. Handy when pasting from a spreadsheet.

## `maxResultsPerSource` (type: `integer`):

Stop after this many videos for each search keyword, playlist and channel tab. Searches usually stop around 500-600 results because YouTube stops returning more.

## `scrapeVideoDetails` (type: `boolean`):

Also open each video to add likes, comments count, exact publish date, category, full description and tags. Charged as a 'video with details' instead of a 'video'.

## `newerThanDays` (type: `integer`):

Skip videos published more than this many days ago. For channels, scraping stops at the first older video (tabs are newest first), so it is also a cheap way to monitor channels.

## `sortBy` (type: `string`):

Order of search results.

## `uploadDate` (type: `string`):

Only search results uploaded in this period. Results are also checked against the shown upload time, so older videos never slip in.

## `duration` (type: `string`):

Short = under 4 minutes, medium = 4-20 minutes, long = over 20 minutes.

## `searchType` (type: `string`):

Regular videos, or YouTube Shorts only (uses YouTube's own Shorts search filter).

## `language` (type: `string`):

Language code for YouTube texts such as '3 days ago' (e.g. en, de, es). Keep 'en' if you use the parsed numbers and dates.

## `country` (type: `string`):

Two-letter country code. Affects search ranking and which videos are available.

## `maxConcurrency` (type: `integer`):

How many sources or videos to scrape at once.

## `maxRetries` (type: `integer`):

Retries for rate limits, timeouts and server errors, each with a fresh proxy IP.

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

Apify Proxy (datacenter) works well. If YouTube rate-limits, the Actor switches to residential IPs automatically.

## Actor input object example

```json
{
  "searchQueries": [
    "espresso machine review"
  ],
  "channels": [
    "@mkbhd"
  ],
  "channelContent": [
    "videos"
  ],
  "maxResultsPerSource": 50,
  "scrapeVideoDetails": false,
  "sortBy": "relevance",
  "uploadDate": "any",
  "duration": "any",
  "searchType": "video",
  "language": "en",
  "country": "US",
  "maxConcurrency": 4,
  "maxRetries": 5,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

All dataset rows (status ok or error).

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

JSON summary: processed/ok/failed counts, error codes, events charged, HTTP stats.

# 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 = {
    "searchQueries": [
        "espresso machine review"
    ],
    "channels": [
        "@mkbhd"
    ],
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapemole/youtube-video-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 = {
    "searchQueries": ["espresso machine review"],
    "channels": ["@mkbhd"],
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapemole/youtube-video-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 '{
  "searchQueries": [
    "espresso machine review"
  ],
  "channels": [
    "@mkbhd"
  ],
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call scrapemole/youtube-video-channel-scraper --silent --output-dataset

```

## MCP server setup

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