# YouTube Comments Scraper 💬 Replies, Likes & Author Data (`gatherworks/youtube-comments-scraper`) Actor

Extract every comment and reply from any YouTube video: text, like counts, reply counts, timestamps, and author details including verified and creator badges. Sort by top or newest. Super fast, super accurate. No cookies, no login, no API key.

- **URL**: https://apify.com/gatherworks/youtube-comments-scraper.md
- **Developed by:** [GatherWorks](https://apify.com/gatherworks) (community)
- **Categories:**
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: 5.00 out of 5 stars

## 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?

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 💬 Replies, Likes & Authors

**Paste video links. Get back every comment and reply as clean rows — text, real
like counts, reply counts, threading and full author details.**

**No account, no API key, no setup.** Paste links and run.

***

### 🔍 What is the YouTube Comments Scraper?

Give it YouTube links — watch URLs, `youtu.be` links, Shorts, or bare video IDs.
For each video it pulls the comment section and returns one row per comment, with
the video's own context stapled to every row so the dataset stands on its own:

- 💬 the comment text, exactly as posted
- 🔢 likes and reply counts as **numbers**, not display text
- 🧵 replies as their own rows, each pointing at the comment it answers
- 👤 the author, their channel, their avatar, and their badges

### ⚡ What can it do?

- **🔢 Return numbers you can sort.** `4.7M` arrives as `4700000`, so you can
  rank by likes without cleaning the column first.
- **🧵 Rebuild threads without a second pass.** Replies are rows with `isReply:
  true` and `parentCommentId` set, so a thread reassembles with a group-by.
- **✅ Mark verified, creator, pinned and hearted** on the rows they belong to.
- **📦 Take a list of videos** and return one dataset covering all of them.
- **🚧 Cap the spend up front.** `maxComments` is per video and maps one-to-one
  onto what you are charged.
- **🔀 Sort by top or newest.** Newest asks YouTube for the recent end of the
  comment section, so it spans the whole video, not just the first page.
- **🔎 Filter before you pay.** Keep only the comments you want — a like
  threshold, a keyword, verified authors — and rows that do not match are never
  returned and never charged for.
- **📄 Export anywhere.** JSON, CSV, Excel — the dataset is yours.

#### 🎯 Hearts and pins are per-comment, not per-video

`isHearted` is true on the comments the creator actually hearted and false on the
rest — on a 98-comment sample, 5 came back true. `isPinned` behaves the same way.
If you have used a comment export that marked every row as hearted, that is the
difference you are looking at, and it is worth spot-checking against the video.

### ⚡ Speed and accuracy

**Speed.** Measured on a real run: **465 rows from 8 videos in 45 seconds**, zero
failures.

**Accuracy.** Checked on that same run: **zero duplicates, zero missing comment
text, and every reply correctly parented to the comment it answers.** Like and
reply counts are numbers, not display text. Videos with comments switched off are
reported as such rather than returned as empty.

### 📊 What data does it return?

One row per comment, each carrying the video it came from:

| Field | Notes |
|---|---|
| 💬 `text` | The comment itself |
| 👍 `likeCount` | Likes as a number — `4.7M` arrives as `4700000` |
| 🔢 `replyCount` | How many replies the comment has |
| 📅 `publishedTimeText` | Posted, as YouTube displays it |
| 📌 `isPinned` | Pinned to the top by the creator |
| ❤️ `isHearted` | Hearted by the creator — read from the field that actually carries it |
| 👤 `authorName`, `authorChannelId`, `authorAvatarUrl` | Who wrote it |
| ✅ `authorIsVerified`, `authorIsCreator` | Verified badge, and whether it is the video's own channel |
| 🧵 `isReply`, `parentCommentId` | Reply threading, when replies are switched on |
| 🆔 `commentId` | Stable per comment — use it to de-duplicate across runs |
| 🏷️ `videoTitle`, `channelName`, `channelId` | Video context on every row |
| 📊 `viewCount`, `publishedText` | Video views and publish date |
| ⏱️ `scrapedAt` | When the row was captured |

#### 🚫 What it does **not** return

- **Comments from members-only or private videos.** They are not served publicly.
- **Comments on videos with comments disabled.** The video is reported and skipped.
- **Dislikes.** YouTube no longer publishes them, and a guess is not a number.
- **Exact posting timestamps.** YouTube serves relative text ("6 years ago"); it is
  passed through as it is given rather than converted into a date it cannot support.

### 💰 How much does it cost?

You pay per event, for what the run actually produces — never for runtime, and
never on a subscription. `maxComments` is per video and is the control that
matters: comments arrive 20 at a time, so it maps directly onto both requests and
cost.

Standard rate, at **$0.0005 per comment**:

| Run | Cost |
|---|---|
| 🟩 500 comments from 1 video | 💵 $0.25 |
| 🟨 1,000 comments from 1 video | 💵 $0.50 |
| 🟧 1,000 comments + 500 replies | 💵 $0.75 |

A video with comments turned off costs only the base video fee. A video that
fails to load costs nothing. **A month you do not run it costs nothing at all** —
there is no plan to keep alive.

#### What you get for that

| | Included here |
|---|---|
| 🔢 Like and reply counts parsed to numbers | ✅ |
| 🧵 Replies parented to their comment | ✅ |
| ❤️ Creator hearts read from the field that carries them | ✅ |
| 👤 Author channel, avatar and badges | ✅ free |
| 🏷️ Video context stapled to every row | ✅ free |
| 💳 Minimum spend | none |
| 📅 Monthly commitment | none |

The usual alternative is a monthly seat or export plan, priced on how many
exports you are allowed rather than how many comments you actually took, and
charged again next month whether you used it or not. Here you pay for rows.

### 🚀 How do I use it?

1. Paste your video URLs into `videoUrls`.
2. Set `maxComments` to the number you actually need per video.
3. Set `maxRepliesPerComment` above `0` only if you want reply threads.
4. Run it. Rows land in the dataset as they arrive.

### ⚙️ Input

| Input | Type | Default | What it does |
|---|---|---|---|
| 🔍 `videoUrls` | array | — | **Required.** Watch URLs, `youtu.be` links, Shorts URLs or bare 11-character IDs |
| 🔗 `startUrls` | array | — | The same thing under the name other comment Actors use — paste an existing input straight in |
| 🚧 `maxComments` | integer | `100` | Per video. Your main cost control. Also accepted as `maxItems` |
| ✅ `includeReplies` | boolean | `false` | Alias used by other Actors for switching replies on |
| 🔀 `sortBy` | string | `top` | `top` or `newest`. Also accepted as `sort` or `sortCommentsBy` |
| 🔎 `filter` | object | — | Keep only matching comments. Unmatched rows are not returned and not charged |
| 🔍 `maxScanned` | integer | `20×` limit | Only with a filter. How many comments to look at per video before stopping |
| 🧵 `maxRepliesPerComment` | integer | `0` | Set above 0 to collect reply threads |
| 🔢 `concurrency` | integer | `5` | Videos processed at once |
| 🌏 `language`, `country` | string | `en`, `US` | Affects ordering and relative timestamps |
| ⚙️ `proxyConfiguration` | object | — | Optional proxy settings |

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=jNQXAC9IVRw",
    "https://youtu.be/dQw4w9WgXcQ"
  ],
  "maxComments": 500,
  "maxRepliesPerComment": 5,
  "sortBy": "newest",
  "filter": { "likeCount": { "gte": 100 }, "authorIsVerified": true }
}
```

### 📄 Output example

A real row from a real run, not an illustration:

```json
{
  "videoId": "jNQXAC9IVRw",
  "videoUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "videoTitle": "Me at the zoo",
  "channelName": "jawed",
  "channelId": "UC4QobU6STFB0P71PMvOGN5A",
  "viewCount": 407909330,
  "publishedText": "Apr 23, 2005",
  "commentId": "UgzuC3zzpRZkjc5Qzsd4AaABAg",
  "text": "We're so honored that the first ever YouTube video was filmed here!",
  "publishedTimeText": "6 years ago",
  "likeCount": 4700000,
  "replyCount": 985,
  "isPinned": false,
  "isHearted": true,
  "authorName": "@SanDiegoZoo",
  "authorChannelId": "UCC5NfQ6Mf0dq_eEwv4P_hWA",
  "authorAvatarUrl": "https://yt3.ggpht.com/i8RqA9tYqbbnOi4mlnYEY7IR3kXAoDqQbM_5Sfzw7OjTubiGsOaTdlrIZmj-I0q0Vup68eqs=s88-c-k-c0x00ffffff-no-rj",
  "authorIsVerified": true,
  "authorIsCreator": false,
  "isReply": false,
  "parentCommentId": null,
  "scrapedAt": "2026-08-29T06:22:33.271Z"
}
```

Replies arrive as their own rows with `isReply: true` and `parentCommentId` set to
the comment they answer, so you can rebuild threads without a second pass.

### ❓ Frequently asked questions

**Do I need a Google account or API key?**
No. There is nothing to sign up for and no key to manage.

**Can I get replies to replies?**
YouTube's comment section is two levels deep — comments and their replies. There
is no third level to fetch.

**Why are some comments missing?**
YouTube serves the comment section in its own order and does not guarantee every
comment is reachable. `maxComments` also caps the run. Both are reported, never
silently trimmed.

**What can I filter on?**
Any field in the table above. Conditions take an operator — `gte`, `lte`, `eq`,
`ne`, `contains`, `notContains`, `startsWith`, `endsWith`, `matches`, `in`,
`notIn`, `exists`, `before`, `after` — or a bare value for an exact match. Give
several and all must hold; use `{"any": [...]}` for or.

**Am I charged for comments the filter removes?**
No. The filter runs before the row is returned, so you are only charged for
comments you actually receive.

**My filter returned fewer comments than I asked for.**
A narrow filter can run out of comments to look at before it fills your limit.
The run reports how many it looked at and how many matched; raise `maxScanned`
to search further. Searching further takes longer but costs no more.

**Can I run the same videos again later?**
Yes. `commentId` is stable, so you can de-duplicate against a previous run and
keep only what is new.

### 🏷️ Pricing

Pay per event — you are charged for output, not for platform time.

| Event | Price | Covers |
|---|---|---|
| 💬 `comment` — **primary** | 💵 $0.0005 | One comment returned. The headline rate |
| 📹 `video-processed` | 💵 $0.001 | One video handled, including videos with comments turned off |
| 🧵 `reply` — **add-on** | 💵 $0.0005 | One reply. Only charged when replies are switched on |
| 🏷️ Video title, channel and view count | ✅ **free** | On every row |
| ▶️ `apify-actor-start` | 💵 $0.00005 | Starting one run, charged once per GB of memory |
| 🔢 Number parsing, threading, author badges | ✅ **free** | On every row |
| 🔎 Filtering | ✅ **free** | Filtered-out comments are not charged for |
| 🔀 Sorting | ✅ **free** | Top or newest |

Replies are the only optional event. Leave `maxRepliesPerComment` at `0` and you
are charged for comments and nothing else.

#### 🏅 Rates by tier

The prices above are the standard rate. Higher Apify plans are billed at the
discounted tiers below, automatically:

| Event | FREE | BRONZE | SILVER | GOLD |
|---|---|---|---|---|
| 💬 `comment` | 💵 $0.000500 | 💵 $0.000467 | 💵 $0.000433 | 💵 $0.000400 |
| 📹 `video-processed` | 💵 $0.001000 | 💵 $0.000933 | 💵 $0.000867 | 💵 $0.000800 |
| 🧵 `reply` | 💵 $0.000500 | 💵 $0.000467 | 💵 $0.000433 | 💵 $0.000400 |
| ▶️ `apify-actor-start` | 💵 $0.000050 | 💵 $0.000050 | 💵 $0.000050 | 💵 $0.000050 |

#### 🛑 You are never billed past your limit

The run watches its own charge limit and stops collecting when it reaches it, so
a large `maxComments` on a large video cannot quietly overshoot the budget you set
on the run.

# Actor input Schema

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

Video links or IDs. Accepts watch URLs, youtu.be links, Shorts URLs and bare 11-character IDs.

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

Same as the field above, under the name other YouTube comment Actors use. Paste an existing input straight in - a list of plain URL strings or of <code>{"url": "..."}</code> objects are both accepted. Combined with any videos listed above.

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

Stops each video once this many comments have been collected. This is your main cost control.

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

Set above 0 to also collect replies. Each reply is billed as an add-on. Leave at 0 to collect top-level comments only.

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

Which comments to collect first. <b>Top</b> is YouTube's own ranking; <b>Newest</b> starts from the most recent. Also accepted as <code>sort</code> or <code>sortCommentsBy</code>.

## `sort` (type: `string`):

Alias for the field above, under the name other Actors use. Accepts <code>top</code>, <code>latest</code> or <code>newest</code>.

## `sortCommentsBy` (type: `string`):

Alias for the field above, under the name other Actors use.

## `filter` (type: `object`):

Only keep comments matching these conditions. Rows that do not match are not returned and are not charged for. Example: <code>{"likeCount": {"gte": 100}, "text": {"contains": "great"}, "authorIsVerified": true}</code>. Operators: eq, ne, gt, gte, lt, lte, contains, notContains, startsWith, endsWith, matches, in, notIn, exists, before, after.

## `maxScanned` (type: `integer`):

Only applies when a filter is set. How many comments to look at per video before stopping. Defaults to 20x the comment limit. Searching further takes longer but costs no more.

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

Two-letter language code used for comment ordering and relative timestamps.

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

Two-letter country code. Affects which comments YouTube surfaces first.

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

Optional proxy settings. Leave as they are unless you have a reason to change them.

## `concurrency` (type: `integer`):

How many videos to process at once. Raise it for a large list, lower it if you prefer a steadier run.

## `maxItems` (type: `integer`):

Alias for the maximum comments field, under the name other Actors use. Whichever of the two is set lower wins.

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

Alias used by other Actors. Turning this on collects reply threads; the numeric field above gives you finer control over how many.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "startUrls": [],
  "maxComments": 100,
  "sortBy": "top",
  "language": "en",
  "country": "US",
  "proxyConfiguration": {
    "useApifyProxy": true
  },
  "concurrency": 5
}
```

# Actor output Schema

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

Every comment collected, with like and reply counts, author details and the video it came from.

# 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": 100,
    "maxRepliesPerComment": 0,
    "sortBy": "top",
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("gatherworks/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=dQw4w9WgXcQ"],
    "maxComments": 100,
    "maxRepliesPerComment": 0,
    "sortBy": "top",
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("gatherworks/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=dQw4w9WgXcQ"
  ],
  "maxComments": 100,
  "maxRepliesPerComment": 0,
  "sortBy": "top",
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call gatherworks/youtube-comments-scraper --silent --output-dataset

```

## MCP server setup

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