# YouTube Comments Scraper — Text, Authors & Likes (`dottti/youtube-comments-scraper`) Actor

Scrape comments from any YouTube video: text, author, like count, reply count and timestamp. No API key, no quota, no login.

- **URL**: https://apify.com/dottti/youtube-comments-scraper.md
- **Developed by:** [Mohanad Alshaka](https://apify.com/dottti) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 comment scrapeds

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 Comments Scraper — Text, Authors & Likes

Scrape comments from any YouTube video: text, author, like count, reply count and timestamp. No API key, no quota, no login.

### Output

```json
{
  "videoId": "dQw4w9WgXcQ",
  "commentId": "Ugzge340dBgB75hWBm54AaABAg",
  "url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&lc=Ugzge340dBgB75hWBm54AaABAg",
  "text": "can confirm: he never gave us up",
  "publishedText": "1 year ago",
  "isReply": false,
  "replyLevel": 0,
  "authorName": "@YouTube",
  "authorChannelId": "UCBR8-60-B28hp2BmDPdntcQ",
  "authorChannelUrl": "https://www.youtube.com/channel/UCBR8-60-B28hp2BmDPdntcQ",
  "authorIsVerified": true,
  "authorIsCreator": false,
  "likeCount": 315000,
  "likeCountText": "315K",
  "replyCount": 963,
  "replyCountText": "963"
}
```

Every comment carries a **direct link** (`&lc=`) that opens it in place on the video.

### The trap this avoids

Comments are not on the watch page. They come from YouTube's internal endpoint in two hops: a request by video ID returns a set of continuation tokens, and one of those opens the comments.

A `next` response carries **several** continuations — related videos, filter chips, and comments. Picking the wrong one returns a page of recommendations that parses to **zero comments**, which looks exactly like a video with comments turned off. This Actor selects the comments continuation specifically, and a test asserts it rejects a related-videos token placed first.

A video that genuinely has comments disabled is reported as such in `RUN_SUMMARY`, not silently as an empty result.

### Counts are abbreviated at source, and say so

YouTube publishes comment like and reply counts as strings like `"315K"`. That is rounded before it ever reaches this Actor. Both forms are returned — `likeCount: 315000` and `likeCountText: "315K"` — so you can see the precision you actually have rather than trusting a number that looks exact.

### Blocked videos are never charged

YouTube refuses some datacenter IPs with "Sign in to confirm you're not a bot". Billing for that would charge you for this Actor's IP being refused, so those rows are written with `blocked: true` and `error: "blocked_by_youtube"` and **no event fires**.

Set `proxyConfiguration` to Apify Proxy with `RESIDENTIAL` groups to avoid it.

### Input

| Field | What it does |
| --- | --- |
| `videos` | URLs or 11-character IDs. Watch, youtu.be, Shorts and embed all work. |
| `maxCommentsPerVideo` | Cap per video, and therefore on cost. About 20 comments arrive per page. |
| `includeReplies` | Off by default, so you get top-level comments only. Replies are marked with `isReply` and `replyLevel`. |
| `language` / `country` | Two-letter codes. |

#### Example

```json
{
  "videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "maxCommentsPerVideo": 500,
  "includeReplies": true
}
```

### Notes and limits

- Public comments only. No login, no cookies, no Google account.
- Comments are returned in YouTube's default order (top comments first), not chronological.
- Timestamps are relative, exactly as YouTube publishes them ("1 year ago"). No absolute date is invented from them.
- Paging is capped at 30 requests per video regardless of `maxCommentsPerVideo`.

### Development

```bash
npm install
npm test
node src/main.js
```

# Actor input Schema

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

YouTube video URLs or bare 11-character IDs. Watch, youtu.be, Shorts and embed URLs all work.

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

Hard cap per video, and therefore on cost. YouTube returns about 20 comments per page.

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

Off by default, so you get top-level comments only. Turn on to include replies, which are marked with isReply and replyLevel.

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

Two-letter language code.

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

Two-letter country code.

## `requestDelayMs` (type: `integer`):

YouTube throttles bursts. Raise this if the log shows 429 or bot checks.

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

Retries with exponential backoff on 403, 429 and 5xx.

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

Required. Comments come from YouTube's internal endpoint, which refuses Apify's datacenter IPs with HTTP 403 — verified 14 September 2026. Residential is the default because the Actor cannot collect comments without it. Blocked videos are never charged.

## Actor input object example

```json
{
  "videos": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "maxCommentsPerVideo": 100,
  "includeReplies": false,
  "language": "en",
  "country": "US",
  "requestDelayMs": 700,
  "maxRetries": 4,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

## `comments` (type: `string`):

Video ID, comment ID and direct link, comment text, published time, reply level and isReply flag, author name, channel ID, channel URL, avatar, verified and creator flags, plus like and reply counts as both numbers and YouTube's own abbreviated strings.

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

Per-video outcome: comments delivered, pages fetched, videos with comments disabled, and any video that failed or was blocked.

# 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 = {
    "videos": [
        "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dottti/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 = { "videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"] }

# Run the Actor and wait for it to finish
run = client.actor("dottti/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 '{
  "videos": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ]
}' |
apify call dottti/youtube-comments-scraper --silent --output-dataset

```

## MCP server setup

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