# YouTube Scraper: Videos, Shorts, Channels & Comments (`subimpact/youtube-scraper`) Actor

Fast YouTube scraper with no browser and no API key: keyword search with filters, channel videos and Shorts, channel About data, playlists, video details (exact likes, date, description) and comments with replies. $1.00 per 1,000 results.

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

## Pricing

from $1.00 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

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, Shorts, Channels & Comments

Fast, keyless YouTube scraper built on plain HTTPS calls to YouTube's internal API. No key, no login,
no headless browser. Keyword search (with sort and date filters), channel videos and Shorts tabs,
playlists, single video details (exact likes, exact publish date, full description, comment count) and
comment scraping, all in one run.

**Pay per result: $1.00 per 1,000 results.** You are charged $0.001 per delivered row and never for empty
or failed work. Platform usage (compute, memory) is billed separately by Apify and is small because the
actor is a lightweight HTTP client, not a browser.

### How this compares

Plain HTTPS calls to YouTube's internal API: no key, no login and no headless browser, so the compute you pay for is small. One run covers keyword search with sort and date filters, channel videos and Shorts tabs, playlists, single video details and comments. At $1.00 per 1,000 results it is below the four most-used alternatives in this lane: streamers/youtube-scraper ($4.00) and streamers/youtube-shorts-scraper ($4.00), streamers/youtube-comments-scraper ($2.00) and streamers/youtube-channel-scraper ($1.30). Transcripts and media downloads are not offered (see the caveats below); pintostudio/youtube-transcript-scraper covers transcripts at $10.00 per 1,000 items.

| Actor | Price per 1,000 results | Platform usage |
|---|---|---|
| subimpact/youtube-scraper | $1.00 | paid by you |
| streamers/youtube-scraper | $4.00 | see listing |
| streamers/youtube-comments-scraper | $2.00 | see listing |
| streamers/youtube-channel-scraper | $1.30 | see listing |
| streamers/youtube-shorts-scraper | $4.00 | see listing |

Prices are Apify Store list prices on the Free plan, checked 24 October 2026. Competitor prices can change; check their listings.

### What it scrapes

| Mode | Source | You get |
|---|---|---|
| Keyword search | `searchQueries` | Videos (and optionally Shorts) with title, duration, views, publish text, channel, thumbnail |
| Channel URL | `startUrls` (`/@handle`, `/channel/`, `/c/`, `/user/`) | Videos-tab or Shorts-tab items (or both), plus one channel row with subscribers, lifetime views, About info and external links |
| Playlist URL | `startUrls` (`?list=`) | Videos in playlist order |
| Video URL | `startUrls` (`/watch?v=`, `youtu.be`, `/shorts/`, `/live/`) | Full video details: exact likes, exact publish date, full description, comment count |
| Comments | `scrapeComments` | Top or Newest comments with author, like count, reply count, and optionally replies |

### Input modes

```json
{
  "searchQueries": ["python tutorial", "coldplay viva la vida"],
  "searchSortBy": "views",
  "searchUploadDate": "year",
  "maxResultsPerQuery": 100
}
```

```json
{
  "startUrls": [{ "url": "https://www.youtube.com/@MrBeast" }],
  "channelContent": "both",
  "maxVideosPerChannel": 200,
  "includeChannelInfo": true
}
```

```json
{
  "startUrls": [{ "url": "https://www.youtube.com/playlist?list=PL4fGSI1pDJn6puJdseH2Rt9sMvt9E2M4i" }],
  "maxVideosPerChannel": 500
}
```

```json
{
  "startUrls": [{ "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ" }],
  "scrapeComments": true,
  "commentsSortBy": "top",
  "maxCommentsPerVideo": 100,
  "includeReplies": true,
  "enrichVideoDetails": true
}
```

You can mix modes: `searchQueries` and `startUrls` run together in one run.

### Output fields

One dataset row per item. `itemType` is one of `video`, `short`, `channel`, `comment` (videos and Shorts
are the same shape; a Short always has `isShort: true` and no duration because YouTube does not show one
in the Shorts grid). Every row also carries `source`, `input`, `position` (rank within its input) and
`scrapedAt`.

Videos and Shorts:

| Field | Type | Notes |
|---|---|---|
| `videoId`, `url`, `title` | string | canonical `https://www.youtube.com/watch?v=` URL |
| `description` | string/null | full description, only with `enrichVideoDetails` or a video URL |
| `durationText`, `durationSeconds` | / integer | `6:15:56` / `22556` |
| `viewCount`, `viewCountText` | integer / string | `922364`, `922,364 views` |
| `likeCount`, `likeCountText` | integer / string | exact `19491363` only with `enrichVideoDetails`; rounded `19M` otherwise when shown |
| `publishedText`, `publishedDate` | string / ISO date | `4 days ago`; exact `2009-10-24` when available |
| `channelId`, `channelName`, `channelHandle`, `channelUrl` | string/null | |
| `channelSubscriberCount`, `channelSubscriberCountText` | integer / string | only when channel info was fetched |
| `channelVerified` | boolean/null | verification badge |
| `thumbnailUrl` | string | highest-resolution thumbnail YouTube served |
| `isLive` | boolean | true for live streams |
| `badges` | array/null | e.g. `["New", "4K"]` |
| `commentCount`, `commentCountText` | integer / string | only with `enrichVideoDetails` or video URL |
| `hashtags` | array/null | extracted from the description |

Channels (one row per channel URL when `includeChannelInfo`):

| Field | Type | Notes |
|---|---|---|
| `channelId`, `channelName`, `channelHandle`, `channelUrl` | string/null | |
| `channelDescription`, `avatarUrl` | string/null | |
| `subscriberCount`, `subscriberCountText` | integer / string | `522000000`, `522M subscribers` |
| `videoCount`, `totalViewCount` | integer | `1008`, lifetime views |
| `country`, `joinedDate` | string | `United States`, `2012-02-19` |
| `links` | array/null | `[{ "title": "...", "url": "..." }, ...]` |

Comments:

| Field | Type |
|---|---|
| `commentId`, `videoId`, `text` | string |
| `author`, `authorChannelId` | string |
| `authorIsVerified`, `authorIsCreator` | boolean |
| `likeCount`, `likeCountText`, `replyCount` | integer |
| `publishedText` | string (e.g. `6 years ago (edited)`) |
| `isReply`, `parentCommentId` | boolean / string |

Comments get author handles exactly as YouTube displays them (e.g. `@grustache4569`).

### Limits and honest caveats

- **No transcripts and no timed captions.** Those endpoints are blocked from datacenter IPs; the actor
  does not call them.
- **No video or audio download.** The `player` endpoint is blocked for anonymous datacenter access, so
  stream URLs cannot be provided.
- **Duration for a bare video URL** comes from a search lookup (`search` by the video id and an
  exact-match read) and can be `null` when the video is too new to appear in search.
- **Counts like "119M views" or "522M subscribers" are YouTube's rounded display figures.** Exact view
  counts are available for videos with details; exact subscriber counts are never exposed without login.
- **Exact like counts are only available** when the video page itself is fetched (default for video URLs,
  optional via `enrichVideoDetails` for search/channel results; one extra request per video).
- `maxTotalResults` (default 1,000) is a hard global stop on all rows so a single run cannot snowball.
- Proxies are **optional**: the scrape works over plain HTTPS from datacenter IPs. Enable Apify Proxy only
  for very high volumes if you see rate limiting (429s are retried automatically with backoff).

### FAQ

**Do I need an API key?** No.

**Is a proxy required?** No, but you can enable one: `{"proxyConfiguration": {"useApifyProxy": true}}`.

**What does it cost?** $1.00 per 1,000 delivered results ($0.001 each), plus small platform usage.
You are never charged for rows that failed to be delivered.

**Can I get comments only?** Yes: pass a video URL with `scrapeComments` and the video row (1 result)
plus comment rows.

**Can I mix search and URLs?** Yes: `searchQueries` and `startUrls` are combined in a single run,
processed with up to `maxConcurrency` inputs at a time.

# Actor input Schema

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

Keyword searches to run. Each query yields up to 'maxResultsPerQuery' results.

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

YouTube URLs: videos (/watch?v=, youtu.be), Shorts (/shorts/), channels (@handle, /channel/, /c/, /user/) or playlists (?list=).

## `maxResultsPerQuery` (type: `integer`):

Maximum rows per search query.

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

Maximum video/short rows per channel or playlist URL.

## `channelContent` (type: `string`):

Which tab to scrape for channel URLs ('videos', 'shorts' or 'both'). A /@handle/shorts URL always forces Shorts.

## `includeChannelInfo` (type: `boolean`):

For every channel URL, also deliver one 'channel' row with subscriber counts, About-panel info and links.

## `searchSortBy` (type: `string`):

Sort order for search results.

## `searchUploadDate` (type: `string`):

Upload-date filter for search results.

## `searchIncludeShorts` (type: `boolean`):

Also extract Shorts rows from search result shelves.

## `enrichVideoDetails` (type: `boolean`):

Fetch the video page for every result for exact like counts, exact publish date, full description and comment count. One extra request per video.

## `scrapeComments` (type: `boolean`):

Also scrape comments for each video (adds 'comment' rows to the dataset).

## `maxCommentsPerVideo` (type: `integer`):

Maximum TOP-LEVEL comment rows per video. Replies are capped separately by Max Replies Per Comment and do not count toward this limit.

## `commentsSortBy` (type: `string`):

Comment order: 'top' (most upvoted) or 'newest'.

## `includeReplies` (type: `boolean`):

Also scrape replies to comments.

## `maxRepliesPerComment` (type: `integer`):

Maximum reply rows per comment thread. Reply rows do not count toward Max Comments Per Video.

## `maxTotalResults` (type: `integer`):

Global cap on total rows across all inputs. Hard stop: no rows past this number.

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

How many inputs are processed in parallel. Pagination inside one input is sequential.

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

Interface language code (hl), e.g. 'en'.

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

Two-letter country code (gl), e.g. 'US'.

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

Optional. Proxies are usually not needed: the scrape works over plain HTTPS from datacenter IPs. Enable Apify Proxy only for high volumes.

## Actor input object example

```json
{
  "searchQueries": [
    "python tutorial"
  ],
  "startUrls": [
    {
      "method": "GET",
      "url": "https://www.youtube.com/@MrBeast"
    }
  ],
  "maxResultsPerQuery": 20,
  "maxVideosPerChannel": 20,
  "channelContent": "videos",
  "includeChannelInfo": true,
  "searchSortBy": "relevance",
  "searchUploadDate": "any",
  "searchIncludeShorts": false,
  "enrichVideoDetails": false,
  "scrapeComments": false,
  "maxCommentsPerVideo": 20,
  "commentsSortBy": "top",
  "includeReplies": false,
  "maxRepliesPerComment": 5,
  "maxTotalResults": 1000,
  "maxConcurrency": 5,
  "language": "en",
  "country": "US",
  "proxyConfiguration": {
    "useApifyProxy": false
  }
}
```

# Actor output Schema

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

No description

# 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": [
        "python tutorial"
    ],
    "startUrls": [
        {
            "method": "GET",
            "url": "https://www.youtube.com/@MrBeast"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("subimpact/youtube-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": ["python tutorial"],
    "startUrls": [{
            "method": "GET",
            "url": "https://www.youtube.com/@MrBeast",
        }],
}

# Run the Actor and wait for it to finish
run = client.actor("subimpact/youtube-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": [
    "python tutorial"
  ],
  "startUrls": [
    {
      "method": "GET",
      "url": "https://www.youtube.com/@MrBeast"
    }
  ]
}' |
apify call subimpact/youtube-scraper --silent --output-dataset

```

## MCP server setup

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