# YouTube Comments Scraper – Text, Author, Likes & Replies (`moyadata/youtube-comments-scraper`) Actor

Scrape YouTube video comments -- text, author, likes and reply count -- by video URL or ID. No proxies, no browser, no bans.

- **URL**: https://apify.com/moyadata/youtube-comments-scraper.md
- **Developed by:** [SERP King](https://apify.com/moyadata) (community)
- **Categories:** Social media, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 comments

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?

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 Comments Scraper – Text, Author, Likes & Replies

Scrape comments from any YouTube video — text, author, likes, and reply count — by video URL or ID. Paste a video URL, a `youtu.be` link, a Shorts link, or just the 11-character video ID. This actor calls a licensed SERP data provider directly — **no proxies, no headless browser, no YouTube rate limits or IP bans.**

### What data you get

Each result item is one comment:

- `videoId`, `videoUrl` — the video's ID and canonical `youtube.com/watch?v=` URL
- `videoTitle` — the video's title
- `videoCommentsTotal` — YouTube's displayed total comment count for the video (not how many this actor returned — see Limits)
- `commentRank` — the comment's position in the order YouTube/DataForSEO returned it, `1` being first
- `author`, `authorUrl`, `authorThumbnail` — the commenter's handle, channel URL, and avatar image URL
- `text` — the comment's text
- `publishedAt` — ISO 8601 UTC timestamp, derived from the relative age YouTube shows (see Limits: the date is reliable, the time of day is not)
- `publishedAgo` — YouTube's own human-readable relative time (e.g. `"6 years ago"`, `"3 days ago (edited)"`)
- `likes` — like count on the comment
- `replyCount` — number of replies to the comment (reply text itself is not returned — see Limits)
- `scrapedAt` — ISO 8601 UTC timestamp of when this actor fetched the comment

### Why this scraper

- **No bans, ever** — this actor never touches youtube.com directly. It calls a licensed SERP data provider's comments API, so there's no IP to block and no CAPTCHA to solve.
- **No browser, no proxies** — no headless Chrome to keep patched, no residential proxy bill on top of the actor's own price.
- **Per-comment pricing** — you pay for comments actually returned, not per video or per run (see Pricing).
- **Fast by default** — `mode: "auto"` picks `fast` (live, a few seconds per video) for runs of 50 videos or fewer, and `standard` (queued, cheaper) for bigger batches — see Modes.
- **Batch-friendly** — up to 1,000 videos per run.
- **No silent gaps** — every video that doesn't produce comments (comments disabled, video not found, a stuck task) is listed in the `FAILED_VIDEOS` record with why, so a batch of 200 URLs never comes back as an unexplained 183 videos' worth of rows.

### Use cases

- **Social listening / audience research** — pull what viewers are actually saying about a video, a product mention, or a competitor's content.
- **Sentiment and topic analysis** — feed `text` into an LLM or a classifier at scale across many videos.
- **Community management prep** — surface the most-liked or most-replied comments on your own videos without opening YouTube Studio one video at a time.

### Pricing

This actor uses Apify's **pay-per-event** pricing, flat across all plans. You are charged for:

| Event | What it means | Price |
|---|---|---|
| `apify-actor-start` | Apify's platform-billed "run started" event, charged once per run automatically — no code call needed | $0.00005 |
| `apify-default-dataset-item` ("Comment" in Console) | Charged once per comment written to the output dataset | $0.0012 |

**Videos with no comments are never charged** — they don't produce any dataset rows at all, so you only pay for comments you actually get.

**Worked example:** scraping 1,000 comments costs `1,000 × $0.0012 + $0.00005 ≈ $1.20`.

Apify's free plan includes monthly platform credit that, on its own, covers roughly the first **~4,000 comments** with this actor before you'd need to add a payment method.

### Modes

| Mode | Upstream call | Wall-clock | Cap | When to use |
|---|---|---|---|---|
| `auto` (default) | `fast` for ≤50 videos, `standard` above that | See below | 1,000 videos/run | Picks the right mode for the batch size automatically — no need to think about it for typical runs. |
| `standard` | Queued (`task_post` + polling) | ~5 minutes per batch of up to 100 videos | 1,000 videos/run | Cheapest upstream cost per comment; the right choice once `auto` would pick it anyway (>50 videos). |
| `fast` | Live (synchronous) | A few seconds per video | 50 videos/run | Small batches where you need comments back immediately (interactive use, a quick one-off check). Capped at 50 videos per run so a bad input doesn't run up a live-mode bill. |

**Expected total wall-clock** (with `auto`, or an explicit mode): 10 videos ≈ a few seconds (`fast`); 100 videos ≈ ~5 minutes (`standard`, one batch); 1,000 videos ≈ ~50 minutes (`standard`, 10 batches processed one after another).

### Input

Minimal example:

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxCommentsPerVideo": 100
}
```

| Field | Type | Default | Description |
|---|---|---|---|
| `videoUrls` | array of strings | (see prefill) | YouTube video URLs — `watch`, `youtu.be`, Shorts, or embed links — or bare 11-character video IDs. Max 1,000 per run, 50 when `mode` is explicitly set to `"fast"`. |
| `maxCommentsPerVideo` | integer | `20` | How many comments to return per video, 20-200 (DataForSEO's hard ceiling — see Limits). Any value in that range is accepted; internally it's rounded up to the nearest multiple of 20 for the upstream request (DataForSEO bills per SERP of up to 20 comments), then the results are truncated back down to the number you asked for. Asking for 45 bills 3 upstream pages but returns exactly 45 rows. |
| `mode` | string | `"auto"` | `"auto"`, `"standard"`, or `"fast"` — see Modes. |
| `language` | string | `"en"` | Interface language code DataForSEO uses when fetching the page (e.g. `"en"`, `"es"`). This does not filter which comments come back by language — YouTube returns whatever comments exist regardless of this setting. |
| `country` | string | `"US"` | ISO-2 country code setting the YouTube location context the request is made from (same effect as browsing YouTube from that country). Does not restrict which comments come back. One of a fixed, verified list of ~34 countries (see the Console dropdown). |

### Output

A real dataset item, captured from an actual run against "Me at the zoo" (`jNQXAC9IVRw`, YouTube's first-ever upload) in September 2026:

```json
{
  "videoId": "jNQXAC9IVRw",
  "videoUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "videoTitle": "Me at the zoo",
  "videoCommentsTotal": 10610900,
  "commentRank": 1,
  "author": "@SanDiegoZoo",
  "authorUrl": "https://www.youtube.com/@SanDiegoZoo",
  "authorThumbnail": "https://yt3.ggpht.com/i8RqA9tYqbbnOi4mlnYEY7IR3kXAoDqQbM_5Sfzw7OjTubiGsOaTdlrIZmj-I0q0Vup68eqs=s88-c-k-c0x00ffffff-no-rj",
  "text": "We're so honored that the first ever YouTube video was filmed here!",
  "publishedAt": "2020-09-08T17:48:01Z",
  "publishedAgo": "6 years ago",
  "likes": 4700000,
  "replyCount": 985,
  "scrapedAt": "2026-09-08T17:48:06Z"
}
```

| Field | Type | Description |
|---|---|---|
| `videoId` | string | 11-character YouTube video ID |
| `videoUrl` | string | Canonical `https://www.youtube.com/watch?v=<id>` URL |
| `videoTitle` | string | null | Video title |
| `videoCommentsTotal` | integer | YouTube's displayed total comment count for the video — almost always far larger than what this actor returns (this row's video has 10.6M comments total; this actor returns at most 200 — see Limits) |
| `commentRank` | integer | Position in the order returned, `1` being first |
| `author` | string | null | Commenter's YouTube handle (includes the leading `@`) |
| `authorUrl` | string | null | Commenter's channel URL |
| `authorThumbnail` | string | null | Commenter's avatar image URL |
| `text` | string | The comment's text |
| `publishedAt` | string | null | ISO 8601 UTC, derived from the relative age (`publishedAgo`). The date is accurate to the day for recent comments and to the year for old ones; the time of day is not meaningful. `null` if it didn't parse |
| `publishedAgo` | string | null | YouTube's own human-readable relative time, exactly as displayed |
| `likes` | integer | Like count on the comment |
| `replyCount` | integer | Number of replies — the replies' text itself is not returned (see Limits) |
| `scrapedAt` | string | ISO 8601 UTC timestamp of when this actor fetched the comment |

### Limits

- **200 comments per video, maximum.** DataForSEO's comments API caps `depth` (its pagination parameter) at 200 for both the live and standard queue. This actor never returns more than 200 comments for a single video, no matter how many total comments it has (the real row above is from a video with 10,610,900 comments — this actor would return at most 200 of them). This is the main trade-off against a browser-based scraper that can, in principle, paginate through every comment; it is also why this actor is fast and never gets blocked.
- **Reply text is not returned, only `replyCount`.** DataForSEO's comments endpoint reports how many replies a comment has but does not return the nested reply text itself.
- **`publishedAt` is derived, not exact.** YouTube shows comment age as text ("6 years ago", "2 days ago") and the data source converts that to a timestamp at scrape time. A comment shown as "2 days ago" gets a timestamp exactly 48 hours before the scrape, so the date is usable and the clock time is not. Use `publishedAgo` when you need the raw label; do not use `publishedAt` for minute-level time-series.
- **Sort order is not configurable.** Comments come back in whatever order YouTube/DataForSEO returns them; there is no "newest first" or "top comments" toggle.
- Videos with comments disabled, or that don't exist (deleted, private, region-locked), produce **no dataset rows and are not charged**. They're listed in the `FAILED_VIDEOS` key-value store record (`videoId`, `videoUrl`, `status` — `no_comments`, `timed_out`, or `error` — and a `message`), and counted in `STATS` under `videosWithoutComments`.
- `standard` mode (and `auto` above 50 videos): up to 1,000 videos per run, processed in batches of up to 100, polled for up to 15 minutes per batch. A batch still stuck after 15 minutes is skipped (counted in STATS as `timedOut`, and listed in `FAILED_VIDEOS` with that status) — this is rare and upstream-side.
- `fast` mode, whether picked by `auto` or set explicitly: capped at 50 videos per run (see Modes for why).

### FAQ

**Is this legal?**
The actor retrieves publicly available comment data through a licensed SERP data provider, the same category of data source used by SEO and content-research tools. You are responsible for how you use the extracted text under your local regulations and YouTube's own terms of service.

**How is this different from other YouTube comment scrapers on Apify?**
Most comment scrapers drive a browser or an unofficial YouTube endpoint directly, which means occasional blocks and rate limits, but can in principle pull every comment on a video. This actor calls a licensed SERP data API instead: no proxies, no browser, no bans — at the cost of a 200-comments-per-video ceiling (see Limits). If you need every comment on a handful of videos and don't mind the ban risk, a browser-based scraper may suit you better; if you need reliable, unblockable comment data across hundreds of videos, this actor is the better fit.

**Can I get the replies to a comment?**
Not the reply text — only `replyCount`, the number of replies. DataForSEO's comments API doesn't return nested reply content.

**Do I get charged for videos with no comments?**
No. A video with comments disabled, deleted, or otherwise producing no comments generates no dataset rows, so `apify-default-dataset-item` is never charged for it. Check the `FAILED_VIDEOS` record (alongside `STATS.videosWithoutComments`) to see exactly which videos didn't come back and why.

**Why is `videoCommentsTotal` so much bigger than the number of rows I got?**
`videoCommentsTotal` is YouTube's own displayed total comment count for the video — it's informational context, not a promise of how many this actor returns. This actor is capped at 200 comments per video regardless of how many the video actually has (see Limits).

# Actor input Schema

## `videoUrls` (type: `array`):

YouTube video URLs (watch, youtu.be, Shorts, embed) or bare 11-character video IDs. Max 1,000 per run (50 when `mode` is explicitly set to `fast`).

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

How many comments to return per video, 20-200 (DataForSEO's hard ceiling -- this actor never returns more, see README Limits). Internally rounded up to the nearest multiple of 20 for the upstream request, then the results are truncated back down to this number.

## `mode` (type: `string`):

`auto` (default): `fast` for 50 videos or fewer, `standard` above that. `standard`: queued, cheaper upstream cost. `fast`: live, a few seconds per video -- capped at 50 videos per run.

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

Language code YouTube's interface uses when DataForSEO fetches the page, e.g. "en", "es". Does not filter comments by language -- YouTube returns whatever comments exist regardless of this setting.

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

YouTube location context DataForSEO scrapes from. Does not restrict which comments come back -- it sets the vantage point of the request, same as browsing YouTube from that country.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxCommentsPerVideo": 20,
  "mode": "auto",
  "language": "en",
  "country": "US"
}
```

# Actor output Schema

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

No description

## `stats` (type: `string`):

No description

## `failedVideos` (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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://youtu.be/dQw4w9WgXcQ"
    ],
    "maxCommentsPerVideo": 20,
    "mode": "auto",
    "language": "en",
    "country": "US"
};

// Run the Actor and wait for it to finish
const run = await client.actor("moyadata/youtube-comments-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 = {
    "videoUrls": [
        "https://www.youtube.com/watch?v=jNQXAC9IVRw",
        "https://youtu.be/dQw4w9WgXcQ",
    ],
    "maxCommentsPerVideo": 20,
    "mode": "auto",
    "language": "en",
    "country": "US",
}

# Run the Actor and wait for it to finish
run = client.actor("moyadata/youtube-comments-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 '{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxCommentsPerVideo": 20,
  "mode": "auto",
  "language": "en",
  "country": "US"
}' |
apify call moyadata/youtube-comments-scraper --silent --output-dataset

```

## MCP server setup

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