# YouTube Comment Scraper (`lightmoon/youtube-comment-scraper`) Actor

Export every comment under a YouTube video: text, author with channel link, likes, reply count, date and whether it is pinned, in YouTube's own order. From $0.40 per 1,000. Replies optional, sorted by top or newest, no account or API key needed.

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

## Pricing

from $0.40 / 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.
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 Comment Scraper

Paste video links, get the conversation underneath as a table: comment text,
author with a link to their channel, likes, how many replies it drew, when it
was written, and whether the creator pinned it — **in YouTube's own order**,
with the rank on every row.

No account, no API key, no quota.

### What one row looks like

```json
{
  "videoId": "dQw4w9WgXcQ",
  "videoUrl": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
  "rank": 1,
  "commentId": "Ugzge340dBgB75hWBm54AaABAg",
  "text": "can confirm: he never gave us up",
  "author": "@YouTube",
  "authorChannelId": "UCBR8-60-B28hp2BmDPdntcQ",
  "authorChannelUrl": "https://www.youtube.com/channel/UCBR8-60-B28hp2BmDPdntcQ",
  "authorIsVerified": true,
  "authorIsCreator": false,
  "authorAvatarUrl": "https://yt3.ggpht.com/...",
  "likeCount": null,
  "likeCountText": "319K",
  "replyCount": 962,
  "replyCountText": "962",
  "publishedText": "1 year ago",
  "isReply": false,
  "parentCommentId": null,
  "isPinned": true,
  "pinnedBy": "Pinned by @RickAstleyYT",
  "scrapedAt": "2026-09-25T06:12:03+00:00"
}
```

21 fields. Views in the dataset tab: **Comments**, **Authors**, **Threads**,
**All fields**.

### Two things said up front

**1. YouTube rounds like counts, and this Actor does not un-round them.**
The page says `319K`, so the row says `319K` in `likeCountText` and leaves
`likeCount` empty. A reply count is usually exact, and then both are filled.
The one place the rounded figure is read as a number is the **Minimum likes**
filter — a filter that refused to work without exact counts would refuse to
work at all, and it says so in its own description.

**2. Comments switched off is an answer, not an error.** YouTube says it in
words inside an ordinary 200. The run reads the words, stores nothing for that
video, **charges nothing**, names it in `commentsDisabled` and carries on to
the next one.

### Filters, and what they cost

| filter | what it does |
|---|---|
| **Sort by** | Top (what a visitor sees) or Newest first — YouTube's own two orders |
| **Include replies** | each conversation costs one extra request; every reply is a row that names its parent |
| **Minimum likes** | drops quiet comments **before** the row is stored, so they are never charged |
| **Only comments that started a conversation** | keeps the ones with at least one reply |
| **Language / Country** | `hl` and `gl`, sent to YouTube with the request |

### What it costs to run

YouTube serves **twenty comments per request**, and a video needs one request
before that to open its comment section. So a hundred comments from one video
is six requests; a thousand is fifty-one.

### What it does not do

- **It does not log in**, so it sees what a signed-out visitor sees: no hearts
  from the creator, no hidden or held-for-review comments.
- **It does not search comments across videos.** Give it videos; searching all
  of YouTube for a phrase is our search listing's job.
- **It does not invent exact numbers** — see above.

### Free plan

Everything works on the free plan: the same fields, the same filters. Dropped
and disabled videos cost nothing there either.

# Actor input Schema

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

One per line: a watch link, a youtu.be short link, a Shorts link, or just the eleven-character id. A channel address is skipped with a note - this Actor reads one video at a time.

## `maxComments` (type: `integer`):

A ceiling for the whole run. Comments dropped by a filter are never charged.

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

YouTube serves twenty comments per request, so this is also what a video costs: one request per twenty.

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

YouTube's own two orders. Top is what a visitor sees by default; newest is the one for monitoring.

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

Replies live behind their own request, one per conversation. Each reply is a row like any other and names its parent in `parentCommentId`.

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

Only used when replies are on.

## `minLikes` (type: `integer`):

Keep only comments with at least this many likes. YouTube rounds most counts - it says `319K`, not 319 000 - so this filter reads the rounded figure as a number. The row itself never does: it keeps the words and leaves the number empty.

## `onlyWithReplies` (type: `boolean`):

Drop comments nobody replied to. Dropped rows are free.

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

Two-letter code sent to YouTube as `hl`, e.g. en, de, es.

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

Two-letter code sent as `gl`.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "maxComments": 40,
  "maxCommentsPerVideo": 40,
  "sortBy": "top",
  "includeReplies": false,
  "maxRepliesPerComment": 10,
  "minLikes": 0,
  "onlyWithReplies": false,
  "language": "en",
  "country": "US"
}
```

# Actor output Schema

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

No description

## `authors` (type: `string`):

No description

## `threads` (type: `string`):

No description

## `all` (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=dQw4w9WgXcQ"
    ],
    "maxComments": 40,
    "maxCommentsPerVideo": 40
};

// Run the Actor and wait for it to finish
const run = await client.actor("lightmoon/youtube-comment-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=dQw4w9WgXcQ"],
    "maxComments": 40,
    "maxCommentsPerVideo": 40,
}

# Run the Actor and wait for it to finish
run = client.actor("lightmoon/youtube-comment-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=dQw4w9WgXcQ"
  ],
  "maxComments": 40,
  "maxCommentsPerVideo": 40
}' |
apify call lightmoon/youtube-comment-scraper --silent --output-dataset

```

## MCP server setup

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