# YouTube Comments Scraper: Comments, Replies, Authors (`datagleaner/youtube-comments-scraper`) Actor

YouTube comments scraper: every comment and reply of a video by URL or ID, newest or top, with author, likes, reply count, pinned and hearted flags and an optional keyword filter. $0.40 per 1,000 comments. No login, no API key.

- **URL**: https://apify.com/datagleaner/youtube-comments-scraper.md
- **Developed by:** [Data Gleaner](https://apify.com/datagleaner) (community)
- **Categories:** Videos, For creators, Marketing
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.28 / 1,000 comment or replies

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 Comments Scraper: Comments, Replies, Authors

A YouTube comments scraper and comment extractor. Give it video links or IDs and get every **comment and reply** as flat JSON: text, author, channel link, likes, reply count, age, and whether the comment is pinned or hearted by the channel. Sort by top or newest, cap the number per video, and keep only comments that contain your keywords. **$0.0004 per comment ($0.40 per 1,000)**, and comments your filter drops are never billed. Plain HTTP: no browser, no login, no YouTube API key and no daily quota.

The rows have the same fields every time, so people, spreadsheets, sentiment tools and AI agents can all use them directly. Author channel IDs are included, so the same table can feed a channel-contacts lookup.

### What does the YouTube Comments Scraper do?

For each video you give it, the Actor reads the public comment section, the same data a logged-out visitor sees under the video. It pages through the comments in the order you pick until it has the number you asked for, or until the video runs out.

- **Comments:** `commentId`, `text`, `authorName`, `authorHandle`, `authorChannelId`, `authorChannelUrl`, `likeCount`, `replyCount`, `publishedText`, `publishedAt` (an estimate, see below), `isEdited`, `commentUrl`.
- **Flags:** `isPinned` (pinned by the channel), `isHearted` (hearted by the channel), `isChannelOwner` (written by the video's own channel), `authorVerified`.
- **Replies:** switch on `includeReplies` and each reply comes back as its own row with `isReply: true` and `parentId` pointing at the comment it answers.
- **Video context on every row:** `videoId`, `videoUrl`, `videoTitle`, `videoCommentCount`, and the `inputUrl` you supplied.
- **Keyword filter:** `keywords` keeps only comments whose text contains one of your words or phrases. Matching rows carry `matchedKeywords`. The rest are not returned and not billed.
- **Date filter:** `commentsSince` leaves out older comments. With newest-first order the Actor stops reading a video as soon as a page is older than your date.

It does **not** return video files, captions or transcripts, and it does not post, like or reply to anything. For the transcript of a video, see Related Actors below.

### Use cases

- **Sentiment and audience research:** pull the comments of your own or a competitor's videos and feed them to a sentiment model or a spreadsheet.
- **Product and brand feedback:** filter by product names, "refund", "broken" or "price" to find what viewers complain about or ask for.
- **Content ideas:** read what viewers ask for in the replies and the most-liked comments of a niche's biggest videos.
- **Giveaways and community checks:** list every commenter of a video with their channel link.
- **Moderation and monitoring:** schedule a run with `sortBy: newest` and compare `commentId` with the last run to catch new comments.
- **Datasets for AI and analytics:** thousands of comments per video without managing API quota.
- **Outreach research:** `authorChannelId` and `authorChannelUrl` give you the commenter's public channel, which you can pass on to a contact finder.

### Input

| Field | Meaning |
|---|---|
| `videoUrls` | Video links: `watch?v=`, `youtu.be`, Shorts or live URLs. 11-character video IDs also work. |
| `videoIds` | 11-character video IDs. |
| `datasetId` | Optional. A dataset from another run (for example the YouTube Search Scraper); the video is read from `videoId`, `videoUrl`, `url`, `link` or `inputUrl` of each item. Items that are not videos are skipped. |
| `maxDatasetItems` | How many items to read from the dataset. Default 100. |
| `maxCommentsPerVideo` | Rows per video, **replies included**. Default 100, up to 50,000. |
| `sortBy` | `top` (default: pinned and most liked first) or `newest`. YouTube's own Sort comments menu. |
| `includeReplies` | Also return replies. Default false. |
| `maxRepliesPerComment` | Replies read under one comment. Default 20. |
| `keywords` | Keep only comments that contain at least one of these (not case sensitive). |
| `commentsSince` | Keep only comments newer than `YYYY-MM-DD` (approximate, see below). |
| `maxCommentsScanned` | With a filter: how many comments to read per video while looking for matches. Default 5,000. |
| `country`, `language` | What YouTube is asked for. Dates are only estimated for English. |
| `maxConcurrency`, `requestDelaySecs` | Politeness controls. Defaults of 4 requests at a time and 0.25 s spacing are enough for most runs. |
| `proxyConfiguration` | Optional. Works without a proxy. |

Example: the newest 200 comments and replies under two videos that mention price or refund.

```json
{
  "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ", "https://youtu.be/jNQXAC9IVRw"],
  "sortBy": "newest",
  "maxCommentsPerVideo": 200,
  "includeReplies": true,
  "keywords": ["price", "refund"],
  "maxCommentsScanned": 5000
}
```

An empty input runs a built-in example (5 top comments of one video) so you can see the output format for a fraction of a cent. Any input that names a video runs only what you asked for.

#### Use it after another Actor

Give `datasetId` the dataset of a YouTube Search Scraper or YouTube Channel Scraper run and the Actor reads the comments of every video in it. To chain it automatically, open the source Actor's **Integrations** tab, add this Actor and set its input to:

```json
{"datasetId": "{{resource.defaultDatasetId}}", "maxCommentsPerVideo": 100}
```

### Output

Top-level comment:

```json
{
  "commentId": "UgzuC3zzpRZkjc5Qzsd4AaABAg",
  "videoId": "jNQXAC9IVRw",
  "commentUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw&lc=UgzuC3zzpRZkjc5Qzsd4AaABAg",
  "text": "We're so honored that the first ever YouTube video was filmed at the San Diego Zoo!",
  "authorName": "@SanDiegoZoo",
  "authorHandle": "@SanDiegoZoo",
  "authorChannelId": "UCh...",
  "authorChannelUrl": "https://www.youtube.com/channel/UCh...",
  "authorAvatarUrl": "https://yt3.ggpht.com/...",
  "authorVerified": false,
  "isChannelOwner": false,
  "likeCount": 4800000,
  "replyCount": 986,
  "publishedText": "6 years ago",
  "publishedAt": "2020-10-22",
  "publishedApprox": true,
  "isEdited": false,
  "isReply": false,
  "parentId": null,
  "isPinned": true,
  "isHearted": true,
  "inputUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "videoUrl": "https://www.youtube.com/watch?v=jNQXAC9IVRw",
  "videoTitle": "Me at the zoo",
  "videoCommentCount": 10000000,
  "sortBy": "top",
  "scrapedAt": "2026-10-21T09:12:44Z"
}
```

A reply has `isReply: true`, a `commentId` of the form `<parent>.<reply>`, and `parentId` set to the comment it answers. With `keywords`, a row also has `matchedKeywords`.

#### How exact are the numbers and dates?

YouTube shows comment **ages** rounded (`6 years ago`, `3 hours ago`) and **likes and reply counts** rounded above 1,000 (`4.8M`, `105K`). The Actor returns what YouTube shows: `publishedText` is the age text, `publishedAt` is a date worked out from it and always flagged `publishedApprox: true`, and `likeCount` is the rounded public number as an integer. `commentsSince` uses that estimate, so a cut at an exact date is only as precise as the age YouTube shows.

### Pricing

Pay per event, and nothing else:

| Event | Price | Per 1,000 |
|---|---|---|
| Comment or reply row | **$0.0004** | $0.40 |

There is no start fee and no extra charge for compute or proxies. You pay for rows pushed to the dataset. A video that is private, removed, region-locked or has comments turned off costs nothing. Comments dropped by `keywords` or `commentsSince` cost nothing. The run stops at your maximum charge.

Worked examples:

- 1,000 comments of one video: 1,000 x $0.0004 = **$0.40**. The largest comment scraper on the Apify Store charged $0.0015 per comment when we checked on 2026-10-21, which is $1.50 for the same rows.
- 50 videos, top 100 comments each: 5,000 x $0.0004 = **$2.00**.
- A weekly brand check of 20 videos, newest 50 comments, keyword filter on: only the matching rows are billed, at most 1,000 x $0.0004 = **$0.40**.
- 3 videos with 1,000 rows each, replies included: 3,000 x $0.0004 = **$1.20**.
- The default test (prefill: one video, 20 comments) costs about **$0.008**.

### Use it from the API

```bash
curl -X POST "https://api.apify.com/v2/acts/datagleaner~youtube-comments-scraper/run-sync-get-dataset-items?token=<YOUR_APIFY_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "maxCommentsPerVideo": 50, "sortBy": "newest"}'
```

### Use with Python

`pip install apify-client`, set `APIFY_TOKEN`, run. This input returns 50 comments for $0.02.

```python
import os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])
run = client.actor("datagleaner/youtube-comments-scraper").call(run_input={
    "videoUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
    "maxCommentsPerVideo": 50,
    "sortBy": "newest",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item["likeCount"], item["authorName"], item["text"][:80])
```

### Use with JavaScript / Node.js

`npm install apify-client`, set `APIFY_TOKEN`, run as an ES module.

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('datagleaner/youtube-comments-scraper').call({
    videoIds: ['jNQXAC9IVRw'],
    maxCommentsPerVideo: 50,
    includeReplies: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
for (const row of items) console.log(row.isReply ? '  reply:' : 'comment:', row.text);
```

### Use it from n8n, Make, Zapier or an AI agent

Actor ID: `datagleaner/youtube-comments-scraper`

Minimal input:

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

- **n8n:** add the **Apify** node (`@apify/n8n-nodes-apify`), choose **Run an Actor and get dataset**, set Actor to `datagleaner/youtube-comments-scraper` and paste the input.
- **Make:** the Apify app's **Run an Actor** module, then **Get Dataset Items**. **Watch Actor Runs** can trigger a scenario when a run finishes.
- **Zapier:** the Apify action **Run Actor**, then **Fetch dataset items**.
- **AI agents (MCP):** connect to `https://mcp.apify.com?tools=datagleaner/youtube-comments-scraper`. In Claude Code:

  ```
  claude mcp add --transport http apify "https://mcp.apify.com?tools=datagleaner/youtube-comments-scraper"
  ```

  Then ask in plain language, for example: "Get the newest 200 comments of this video and summarise what people are asking for." An agent without the Actor preloaded can find it with Apify's `search-actors` tool by searching "youtube comments".

### Limits

- **Dates are estimates.** YouTube shows only a rounded age for a comment, so `publishedAt` is approximate and `commentsSince` is too. Estimates are only made for English (`language: "en"`).
- **Likes and reply counts are rounded by YouTube** above 1,000.
- **Replies count toward `maxCommentsPerVideo`.** 100 with replies on is 100 rows in total, comments and replies together, so the cost of a run is easy to predict.
- **Very large videos.** In our tests YouTube's `top` list ended after about 1,200 comments on videos with millions of them, while `newest` kept going past 3,000. For a deep read of a huge video use `sortBy: "newest"`. You get what the page lists.
- **Videos with comments turned off, private, removed, age-restricted or region-locked videos** return nothing and are not billed. The status message counts them.
- **Some comments come back with an empty `text`** when YouTube sends no text for them.
- **Only public content.** No login is used, so held-for-review comments and anything behind a sign-in are not returned. There are no video files, captions or transcripts.
- **Rate limits.** The Actor keeps to 4 requests at a time with a short pause, and backs off when YouTube answers 429. If a very large run is slowed down, turn on `proxyConfiguration` or lower `maxConcurrency`.
- YouTube changes its pages now and then. If a field starts coming back empty, report it and it will be fixed.

### FAQ

**Do I need a YouTube API key or quota?** No. The Actor reads the public pages a logged-out visitor sees, so there is no key and no daily quota.

**How do I get all comments of a video?** Set `maxCommentsPerVideo` high (up to 50,000). The Actor pages through the comments until it runs out. On videos with millions of comments, YouTube's `top` list ends after about 1,200; `newest` goes much deeper.

**How do I get the replies?** Set `includeReplies` to true. Each reply is a separate row with `parentId`.

**How do I get only the latest comments?** `sortBy: "newest"`. Add `commentsSince` to stop at a date.

**How do I find comments about one topic?** Put the words in `keywords`. Only matching comments are returned, and only those are billed. Raise `maxCommentsScanned` if the word is rare.

**Can I scrape the comments of many videos from a search?** Yes. Run the YouTube Search Scraper, then give this Actor its dataset ID in `datasetId`, or chain them from the Integrations tab.

**Can I export to CSV, Excel or JSON?** Yes, from the dataset of the run, in the Apify Console or by API.

**Can I get commenters' emails?** Not from this Actor, and comments carry none. `authorChannelId` points at the commenter's public channel; the YouTube channel email Actor listed below reads published emails from channels.

**Do I need a proxy?** No. It works without one on Apify. The option is there for very large runs.

### Related Actors

- [YouTube Search Scraper](https://apify.com/datagleaner/youtube-search-scraper): find the videos first by keyword, with date, length and sort filters, then pass its dataset to this Actor.
- [YouTube Shorts & Channel Scraper](https://apify.com/datagleaner/youtube-channel-scraper): every video and Short of a channel with views and dates, plus the channel About row. Take the `videoId` values for this Actor from its dataset.
- [YouTube Channel Email Scraper](https://apify.com/datagleaner/youtube-channel-contacts): published emails, websites and socials for channels. Run it on the `authorChannelUrl` values of the commenters you care about.
- [YouTube Transcript Scraper](https://apify.com/datagleaner/youtube-transcript-scraper): the transcript of any video, to read the video and its comments together.

### Responsible use

This Actor reads public YouTube pages without logging in or bypassing any sign-in, and it paces its requests. Use the data in line with YouTube's terms and applicable law. Comments contain names, handles and opinions of real people: if you store them or combine them with other data about individuals, the data-protection obligations are yours.

# Actor input Schema

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

YouTube video links: `https://www.youtube.com/watch?v=...`, `youtu.be`, Shorts or live URLs. 11-character video IDs also work here. Each video is read separately; videos that are private, removed or have comments turned off are skipped and not charged.

## `videoIds` (type: `array`):

11-character video IDs such as `dQw4w9WgXcQ`. Use this or the URL field, whichever your list has.

## `datasetId` (type: `string`):

Optional. Dataset ID of another run. The video is read from the first present field of `videoId`, `videoUrl`, `url`, `link`, `inputUrl`. Items that are not videos (channels, playlists) are skipped. Combine it with the URL fields if you like.

## `maxDatasetItems` (type: `integer`):

How many items to read from the dataset. Each video is then read up to `maxCommentsPerVideo` comments, so this also bounds the cost of a run.

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

Stops after this many rows for each video. Replies count as rows too, so 100 with replies on means 100 comments and replies together. A row is billed only when it is returned. YouTube itself stops listing after a while on videos with millions of comments.

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

The order of YouTube's own Sort comments menu. `top` gives the pinned and most-liked comments first; `newest` gives the latest comments first, which is what you want for monitoring.

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

Also return the replies under each comment (`isReply` true, with `parentId`). Each reply is a billed row and counts toward `maxCommentsPerVideo`.

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

Only used when replies are on. Stops reading replies under one comment after this many.

## `keywords` (type: `array`):

Optional. A comment is returned only if its text contains at least one of these words or phrases (not case sensitive). Comments that do not match are not returned and not billed. Matching rows carry `matchedKeywords`.

## `commentsSince` (type: `string`):

Optional, `YYYY-MM-DD`. Comments older than this are left out and not billed. YouTube shows only a rounded age (`3 months ago`), so the cut is approximate. With newest-first order the Actor stops reading a video as soon as a page is older than the date.

## `maxCommentsScanned` (type: `integer`):

Only used with a keyword or date filter. Filtered-out comments are free, but they still take time to read, so this caps how many comments are read per video while looking for matches.

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

Two-letter country code YouTube is asked for (`US`, `GB`, `DE`).

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

Interface language YouTube is asked for. Comment dates are only converted to `publishedAt` for English (`en`).

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

How many requests run at once. Lower it if you see rate limiting.

## `requestDelaySecs` (type: `number`):

Minimum spacing between requests across the whole run, with random jitter. Raise it if you read many videos and see rate-limit warnings in the log.

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

Optional. Off by default: YouTube answered Apify datacenter IPs in testing. Turn a proxy on if runs start getting refused at high volume; the Actor then switches to a fresh IP after repeated refusals.

## Actor input object example

```json
{
  "videoUrls": [
    "https://www.youtube.com/watch?v=dQw4w9WgXcQ"
  ],
  "maxDatasetItems": 100,
  "maxCommentsPerVideo": 20,
  "sortBy": "top",
  "includeReplies": false,
  "maxRepliesPerComment": 20,
  "maxCommentsScanned": 5000,
  "country": "US",
  "language": "en",
  "maxConcurrency": 4,
  "requestDelaySecs": 0.25
}
```

# Actor output Schema

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

No description

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

No description

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

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

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

```

## MCP server setup

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