# YouTube Comments Scraper – Comments & Replies (`arjun_code/youtube-comments-scraper`) Actor

Export public YouTube comments and replies from videos and Shorts. Get text, GIF/image attachments, authors, likes, dates, and thread links. Choose top comments, newest comments, or public-page collection. No API key. Unofficial; not affiliated with YouTube.

- **URL**: https://apify.com/arjun_code/youtube-comments-scraper.md
- **Developed by:** [ArjunAI Data](https://apify.com/arjun_code) (community)
- **Categories:** Videos, Social media
- **Features:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.50 / 1,000 youtube 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.

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

### Export YouTube comments and replies

Download public comments and replies from YouTube videos and Shorts as structured data for JSON, CSV, Excel, Google Sheets, n8n, and research workflows. Add up to 20 inputs and receive one Dataset item per parent comment or reply, with text, native image/GIF attachments, authors, likes, publication labels, reply relationships, pinned status, creator hearts, badges, and direct comment URLs.

Choose **Top comments** for a ranked sample, **Newest first** for chronological collection, or set the limit to `0` to traverse all public comment pages YouTube makes available. No YouTube Data API key is required.

Invalid inputs and videos that cannot produce a complete result receive a visible, free `videoStatus` record. Previously collected comments remain available if a later request fails. No AI analysis or inferred sentiment is added to the original comments.

> This is an unofficial Actor and is not affiliated with, endorsed by, or sponsored by YouTube or Google. It collects publicly accessible comments only.

#### Ready-to-run examples

- [Analyze top YouTube comments](https://apify.com/arjun_code/youtube-comments-scraper/examples/analyze-top-youtube-comments)
- [Download all public YouTube comments](https://apify.com/arjun_code/youtube-comments-scraper/examples/download-all-public-youtube-comments)
- [Scrape YouTube comments and replies](https://apify.com/arjun_code/youtube-comments-scraper/examples/scrape-youtube-comments-and-replies)

For Shorts, use the [Shorts input example below](#shorts-comments). These examples are separate use cases, not different prices or subscription plans.

### Common use cases

- Brand, product, and campaign monitoring
- Audience research and creator analysis
- Sentiment, topic, and keyword analysis
- Community feedback and moderation review
- Market research and competitor tracking
- NLP, LLM, and data-enrichment workflows

### Input and quick start

#### Supported URLs

| URL type | Example |
| --- | --- |
| Standard video | `https://www.youtube.com/watch?v=VIDEO_ID` |
| Short link | `https://youtu.be/VIDEO_ID` |
| Shorts | `https://www.youtube.com/shorts/VIDEO_ID` |
| Embedded video | `https://www.youtube.com/embed/VIDEO_ID` |
| Live video page | `https://www.youtube.com/live/VIDEO_ID` |
| Bare video ID | `YE7VzlLtp-4` |

For live video pages, the Actor extracts the normal public comments section when available. It does not extract live-chat messages.

Only HTTP(S) YouTube URLs are accepted. A channel URL, a URL from another website, or an invalid ID receives an `invalidInput` status instead of being silently dropped. Different links to the same video ID are processed once. A bare ID does not identify whether content is a Short; use its `/shorts/` URL when `sourceType: "short"` is required.

The input form accepts a plain list of URLs or IDs, one per item. API callers can send a string array such as `"startUrls": ["YE7VzlLtp-4"]` or keep the existing object format `"startUrls": [{"url": "YE7VzlLtp-4"}]`. Both formats reach the same extraction flow.

#### Quick start

Start with a small sample of up to 20 ranked parent comments. Only `startUrls` is required; omitted options use the defaults in the reference table.

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=YE7VzlLtp-4"
  ],
  "maxCommentsPerVideo": 20,
  "sortBy": "top",
  "includeReplies": false,
  "maxRepliesPerThread": 10
}
```

#### Input reference

| Field | Type | Default | Behavior |
| --- | --- | --- | --- |
| `startUrls` | array | Required | 1–20 public video/Shorts URLs or bare 11-character IDs. The form uses a string list; existing `{url: ...}` API entries are also accepted. Duplicate IDs are processed once; invalid entries receive free status rows. |
| `maxCommentsPerVideo` | integer | `100` | `1–5000` limits combined parent and reply records per video. Free status rows do not count. `0` follows all available public parent-comment pages. |
| `sortBy` | string | `top` | `top` for YouTube's ranked view or `newest` for chronological results. Full collection always uses `newest`. |
| `includeReplies` | boolean | `false` | Expand and return replies under each parent comment. Disable for the fastest parent-only run. |
| `maxRepliesPerThread` | integer | `10` | 1–500 replies per parent when `includeReplies` is enabled, including full-collection mode. Ignored when replies are disabled. |

#### Shorts comments

```json
{
  "startUrls": [
    {"url": "https://www.youtube.com/shorts/cjSKS1D9Kow"}
  ],
  "maxCommentsPerVideo": 20,
  "sortBy": "top",
  "includeReplies": false
}
```

#### Comments with replies

```json
{
  "startUrls": [
    {"url": "https://www.youtube.com/watch?v=YE7VzlLtp-4"}
  ],
  "maxCommentsPerVideo": 100,
  "sortBy": "top",
  "includeReplies": true,
  "maxRepliesPerThread": 10
}
```

The limit of 100 counts parents and replies together. It does not mean 100 parents plus their replies. Replies are collected beneath each parent, so a reply-heavy thread can use a significant part of a small result limit.

#### Sorting and full collection

| Result limit | Requested sort | Effective sort | Result |
| ---: | --- | --- | --- |
| Positive number | `top` | `top` | Fixed-size ranked sample |
| Positive number | `newest` | `newest` | Fixed-size chronological sample |
| `0` | `top` or `newest` | `newest` | All available public comment pages |

To follow every available public parent-comment page and include up to 500 replies per parent:

```json
{
  "startUrls": [
    {
      "url": "https://www.youtube.com/watch?v=VIDEO_ID"
    }
  ],
  "maxCommentsPerVideo": 0,
  "sortBy": "newest",
  "includeReplies": true,
  "maxRepliesPerThread": 500
}
```

`maxCommentsPerVideo: 0` removes the combined result limit, not the per-thread reply cap, the run's spending limit, or the platform run timeout. “All” means the public pages YouTube exposes to this unauthenticated session—not private, deleted, held-for-review, or otherwise unavailable comments. Top-ranked pagination can finish before the requested number is reached; a maximum is not a guaranteed result count.

### Dataset output

Each parent comment and included reply is a separate `recordType: "comment"` item.

#### Real comment example

This complete parent-comment record came from a real local run on October 20, 2026. Public counters and relative dates can change.

```json
{
  "recordType": "comment",
  "sourceType": "video",
  "videoId": "YE7VzlLtp-4",
  "videoUrl": "https://www.youtube.com/watch?v=YE7VzlLtp-4",
  "videoTitle": "Big Buck Bunny",
  "videoChannelId": "UCSMOQeBJ2RAnuFungnQOxLg",
  "videoChannelName": "Blender",
  "commentId": "UgyvM3zp3TKN8kEO4Yd4AaABAg",
  "commentUrl": "https://www.youtube.com/watch?v=YE7VzlLtp-4&lc=UgyvM3zp3TKN8kEO4Yd4AaABAg",
  "text": "still looks better than my renders",
  "publishedTimeText": "3 years ago",
  "publishedAtEstimated": "2023-10-20T02:49:36.443192Z",
  "likeCount": 518,
  "likeCountText": "518",
  "replyCount": 7,
  "parentCommentId": null,
  "replyDepth": 0,
  "isReply": false,
  "isPinned": false,
  "isHearted": false,
  "isPaid": false,
  "authorName": "@5MadMovieMakers",
  "authorChannelId": "UCLkZVEXz_qObWr7hAEbS_pQ",
  "authorChannelUrl": "https://www.youtube.com/@5MadMovieMakers",
  "authorAvatarUrl": "https://yt3.ggpht.com/ytc/AIdro_m_HacbRsFWToHwnxwoZRFsLPGdHSq0GCQGcsTkUWV1FXc=s88-c-k-c0x00ffffff-no-rj",
  "authorIsVerified": true,
  "authorIsVideoOwner": false,
  "authorIsMember": false,
  "authorBadges": [
    "Verified"
  ],
  "sortBy": "top",
  "scrapedAt": "2026-10-20T02:49:36.443192Z"
}
```

#### Field groups

| Group | Fields |
| --- | --- |
| Source | `recordType`, `sourceType`, `videoId`, `videoUrl`, `videoTitle`, `videoChannelId`, `videoChannelName` |
| Comment | `commentId`, `commentUrl`, `text`, optional `attachments`, `publishedTimeText`, `publishedAtEstimated` |
| Engagement | `likeCount`, `likeCountText`, `replyCount`, `isPinned`, `isHearted`, `isPaid`, `paidAmountText` |
| Reply relationship | `parentCommentId`, `replyDepth`, `isReply` |
| Author | `authorName`, `authorChannelId`, `authorChannelUrl`, `authorAvatarUrl`, `authorIsVerified`, `authorIsVideoOwner`, `authorIsMember`, `authorBadges` |
| Run context | `sortBy`, `scrapedAt` |

`publishedAtEstimated` is calculated from YouTube's relative label such as `3 years ago`. It is useful for sorting, but it is not an exact publication timestamp supplied by YouTube. `likeCount` is normalized from the displayed value; `likeCountText` preserves the public label.

#### Reply records

A reply has `isReply: true`, `replyDepth: 1`, and its parent's ID in `parentCommentId`. Every reply is independently written to the Dataset and counts as one successful comment result.

This excerpt is from a real creator reply on a Short:

```json
{
  "recordType": "comment",
  "sourceType": "short",
  "videoId": "cjSKS1D9Kow",
  "commentId": "UgxGGjnWNMgVP1KmZnZ4AaABAg.AUH2umSNOznAUH8C_g1c9z",
  "text": "you're so sweet!",
  "likeCount": 10,
  "parentCommentId": "UgxGGjnWNMgVP1KmZnZ4AaABAg",
  "replyDepth": 1,
  "isReply": true,
  "authorName": "@sabrina_ramonov",
  "authorIsVideoOwner": true,
  "commentUrl": "https://www.youtube.com/watch?v=cjSKS1D9Kow&lc=UgxGGjnWNMgVP1KmZnZ4AaABAg.AUH2umSNOznAUH8C_g1c9z"
}
```

#### Image and GIF comments

An image-only comment can legitimately have `text: ""`. Its attachment is preserved instead of inventing text or dropping the comment. This real-record excerpt shows an animated image returned by YouTube:

```json
{
  "recordType": "comment",
  "videoId": "9bZkp7q19f0",
  "commentId": "UgyugAYtlMUjAmOy_1Z4AaABAg",
  "text": "",
  "attachments": [
    {
      "url": "https://i.ytimg.com/c_exp/UgyugAYtlMUjAmOy_1Z4AaABAg/IMSstM98j7Zgs_webp_animated_original_aspect_ratio.webp?sqp=CPzf2tYGGOCoAQ==&rs=AOn4CLA1cE1O1u6u9MfwHlmr1KJaXUeSTA",
      "width": 498,
      "height": 280,
      "format": "IMAGE_FORMAT_ANIMATED_WEBP"
    }
  ]
}
```

`url`, dimensions, and `format` come from YouTube's response. The original format hint is preserved: a GIF-style animation can be served as animated WebP. Attachment URLs can expire. The Actor does not download, host, or convert these files, and no additional event is charged for the attachment.

#### Output table

**Comments overview** keeps the default table compact and includes attachments so image-only comments are not mistaken for missing content. **Full comment and status fields** exposes author details, badges, reply relationships, and status details. Views change the displayed columns, not the underlying records or available export data. Successful comment rows contain data rather than per-request diagnostics.

#### Videos without comment rows

Invalid inputs, empty or disabled comment sections, unavailable videos, partial extractions, failures, and budget stops produce free status records. This example is from the real comments-disabled video:

```json
{
  "recordType": "videoStatus",
  "status": "commentsDisabled",
  "inputValue": "https://www.youtube.com/watch?v=XqZsoesa55w",
  "sourceType": "video",
  "videoId": "XqZsoesa55w",
  "videoUrl": "https://www.youtube.com/watch?v=XqZsoesa55w",
  "commentCount": 0,
  "parentCommentCount": 0,
  "replyCommentCount": 0,
  "truncated": false,
  "errorCode": "COMMENTS_DISABLED",
  "errorMessage": "Comments are turned off. Learn more",
  "suggestion": "Use another public video with comments enabled."
}
```

The example above omits its run timestamp and sort setting for readability.

| Status | Meaning |
| --- | --- |
| `invalidInput` | The entry is not a supported video/Shorts URL or ID. The original input is retained, without an invented video ID. |
| `noComments` | No public comments were returned, without an explicit comments-disabled message. |
| `commentsDisabled` | YouTube explicitly reports that comments are turned off or unavailable. |
| `unavailable` | The video is not accessible to a public session. |
| `partial` | Some comments were saved, but collection could not finish. `truncated` is true. |
| `failed` | Extraction failed before any comments were saved. |
| `chargeLimitReached` | The user-defined spending limit prevented further paid comment records. |

Invalid entries do not prevent valid entries from running. An all-invalid input list produces status records and a run summary instead of a traceback. A budget or network failure does not erase comments already saved. Status rows are not comments and never trigger the `youtube-comment-result` billing event; an Actor start event can still apply.

### Pricing

This Actor uses pay-per-event pricing with platform usage included:

- **Actor start:** `$0.00005` per start event; one event per GB of Actor memory, with a minimum of one.
- **Comment or reply result:** `$0.0005` each—`$0.50` per 1,000 results.
- **Video status row:** free.

Example event charges at the default 512 MB memory, before any configured discounts:

| Comment and reply records | Event charge including one start |
| ---: | ---: |
| 1 | `$0.00055` |
| 100 | `$0.05005` |
| 1,000 | `$0.50005` |

The Actor respects the user's maximum run charge. If the budget cannot cover another comment result, collection stops and the input receives a free `chargeLimitReached` status. The **Pricing** tab is the source of truth for current prices and discounts.

### API and run information

#### Run summary

The Dataset is the primary output. The default Key-Value Store record named `OUTPUT` is a compact run report for automation and troubleshooting. It contains raw input totals, invalid input and duplicate counts, comment totals, and per-source results. Duplicate valid IDs are counted but not fetched again.

Use it when a source produced no comment records and you need to distinguish “no public comments” from “request failed.”

Per-video `stopReason` explains why collection ended, for example `endOfPublicPages`, `maxCommentsReached`, `maxRepliesPerThreadReached`, `maxChargeReached`, `requestBlocked`, or `networkError`. Reaching a configured result/reply cap can produce `status: "complete"` with `truncated: true`: the requested bounded job completed, not the video's entire comment history. These collection details stay in the summary rather than being repeated on every comment.

#### Run through the Apify API

For small runs, the synchronous endpoint returns Dataset items directly:

```bash
curl -X POST \
  "https://api.apify.com/v2/acts/arjun_code~youtube-comments-scraper/run-sync-get-dataset-items" \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "startUrls": [{"url": "https://www.youtube.com/watch?v=YE7VzlLtp-4"}],
    "maxCommentsPerVideo": 100,
    "sortBy": "top",
    "includeReplies": false,
    "maxRepliesPerThread": 10
  }'
```

For large full-collection runs, start the Actor asynchronously and read the default Dataset after completion.

#### Performance guidance

- Start with 10–100 comments when evaluating a source.
- Leave replies disabled if you only need parent comments.
- Use `0` only when complete public pagination is required; popular videos can contain many pages.
- Up to three videos are processed concurrently. Pages for one video remain sequential because YouTube continuation tokens must be followed in order.
- Records are written in small batches, including reply mode. Temporary page-request failures are retried once on that page before an outer retry is considered.
- Full collection can legitimately take longer than a small sample. There is no fixed 40-second deadline for an entire video; the platform run timeout and the user's spending limit still apply.

For context, October 20, 2026 cloud measurements of version 0.1.11 returned 100 parent comments in approximately 8–15 seconds and 1,000 newest parent comments in approximately 55 seconds. These are individual historical samples, not a benchmark or guarantee for this update. Replies, source availability, and network conditions can substantially change runtime.

### Troubleshooting

#### The Dataset contains only a status row

Read `status`, `errorMessage`, and `suggestion`. A video may genuinely have no comments, have comments disabled, be unavailable to a public session, or encounter a temporary request failure.

#### The collected total is lower than YouTube's displayed count

Displayed totals can include deleted, hidden, moderated, unavailable, or non-public comments. Replies are also capped by `maxRepliesPerThread`. The Actor stops when YouTube no longer provides a public continuation token.

#### `includeReplies` is false—does `maxRepliesPerThread` matter?

No. The setting is ignored until replies are enabled.

#### A comment has no text

Inspect `attachments` in the full-field view or JSON export. YouTube supports image/GIF-style comments that do not contain text. Missing text is not filled with a generated description.

#### The run is marked successful, but a source returned a partial result

The run can finish normally after saving available data and a status row. Check each source's `status`, `truncated`, and the `OUTPUT` report rather than treating the process status as proof that every source was complete.

### Related Actors

| Goal | Actor |
| --- | --- |
| Discover videos, Shorts, or channels by keyword | [YouTube Search Scraper](https://apify.com/arjun_code/youtube-search-scraper) |
| Get metadata for known video or Shorts URLs or IDs | [YouTube Video and Shorts Details Scraper](https://apify.com/arjun_code/youtube-video-details-scraper) |
| Enrich known channel handles, IDs, or URLs | [YouTube Channel Details Scraper](https://apify.com/arjun_code/youtube-channel-details-scraper) |
| Export a channel's videos, Shorts, and streams | [YouTube Channel Content Scraper](https://apify.com/arjun_code/youtube-channel-content-scraper) |

### FAQ

#### Does Top comments return every comment?

No. It is YouTube's ranked view. Set `maxCommentsPerVideo` to `0`; the Actor automatically uses Newest first for the most complete public collection.

#### Does this support Shorts?

Yes. Supply a `/shorts/VIDEO_ID` URL. The output uses `sourceType: "short"`.

#### Does this require a YouTube API key?

No.

#### Can it scrape private comments or live chat?

No. It returns comments visible to a public, unauthenticated visitor. It does not access private data, creator dashboards, held-for-review comments, or live chat.

### Support and responsible use

For unexpected results, open the Actor's **Issues** tab and include the run ID, public video URL, input settings, expected behavior, and relevant status or log message. Do not post API tokens or private data.

Use the Actor only for lawful processing of publicly accessible information. Follow applicable laws, privacy requirements, and YouTube's terms. Do not use the output for spam, harassment, or invasive profiling.

# Actor input Schema

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

Enter one public video/Shorts URL or 11-character video ID per item. Supports watch, youtu.be, shorts, embed, and live URLs. Invalid entries receive a free status record; duplicate video IDs are processed once. Existing API inputs with {url: ...} entries are also accepted.

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

Maximum comment and reply records saved per video; free status rows do not count toward this limit. Set to 0 to follow all available public parent-comment pages using Newest first. Included replies remain subject to Maximum replies per comment, the run budget, and YouTube availability.

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

Choose Top comments for a ranked sample or Newest first for chronological results. When Maximum comments per video is 0, the Actor automatically uses Newest first for the most complete collection.

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

Expand replies below each parent comment. Leave disabled for the fastest run.

## `maxRepliesPerThread` (type: `integer`):

Maximum replies saved per parent comment, from 1 to 500. Ignored when Include replies is disabled. This limit still applies when Maximum comments per video is 0; full collection does not mean unlimited replies.

## Actor input object example

```json
{
  "startUrls": [
    "https://www.youtube.com/watch?v=YE7VzlLtp-4"
  ],
  "maxCommentsPerVideo": 100,
  "sortBy": "top",
  "includeReplies": false,
  "maxRepliesPerThread": 10
}
```

# Actor output Schema

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

No description

## `summary` (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 = {
    "startUrls": [
        "https://www.youtube.com/watch?v=YE7VzlLtp-4"
    ]
};

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

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

```

## MCP server setup

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