The Mine Works
← All posts
tutorial October 8, 2026 · 7 min read Updated October 8, 2026

Export a YouTube Video's Comments and Replies to CSV

Pull a YouTube video's comments and their replies into one CSV with YouTube Comments Scraper, each thread rebuilt from parent_comment_id.

Try the scraper

The actor referenced in this article. Pay only for results delivered.

View the scraper →

Comment exports come up whenever someone needs to read an audience at scale: a researcher coding sentiment, a brand team pulling questions from a launch video, a creator collecting the best replies for a follow-up. YouTube shows comments 20 at a time and hides replies behind a click on every thread, so copying even a few hundred by hand takes an afternoon and loses the one thing that makes comments useful: who answered whom.

Try it live: YouTube Comments Scraper: Comments and Replies, No Login. Pay per result delivered. Failed and empty results are never charged.

The YouTube Comments Scraper reads the public comments panel without a login, cookies or an API key. It returns every comment and reply as its own row, in reading order, and links each reply to its parent. That link is what lets you rebuild the threads in a spreadsheet.

What you get and who it is for

Every row carries the video’s id, link and channel plus the comment itself. For a thread-aware export, these are the fields that matter:

FieldWhat it tells you
comment_idYouTube’s id. A reply’s id is its parent’s id, a dot, then the reply part
is_reply, parent_comment_idWhether the row is a reply, and which comment it answers
textFull text with line breaks and emoji
likes, likes_textLikes as a number and as YouTube shows them
reply_countHow many replies YouTube says the comment has
is_pinned, is_heartedPinned by the channel, hearted by the channel
author_is_channel_ownerThe video’s own channel wrote it
published_time_text, published_atRelative time as shown, and an ISO date worked out from it
positionOrder within the video in this run

Researchers use it for sentiment and topic coding, marketers for the questions and objections under a competitor’s review video, and agencies for an engagement check on a creator before paying for a sponsorship. For the coding step, the same route from raw posts to a sentiment score works on comment rows too.

The input

{
  "videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
  "maxComments": 2000,
  "sortBy": "top",
  "includeReplies": true,
  "maxRepliesPerComment": 50
}
  • maxComments is the number of rows per video, top comments and replies together. 2,000 here means 2,000 rows, not 2,000 threads.
  • includeReplies places each comment’s replies right after it, with parent_comment_id set on every reply.
  • maxRepliesPerComment caps the replies read under one comment, from 1 to 1,000. YouTube serves the first 10 replies in one request and about 50 in each later one.
  • sortBy set to top gives YouTube’s default order: the pinned comment first, then the most liked. Use newest if recent comments matter more than popular ones.

Links can be watch links, youtu.be links, Shorts links or bare 11 character video ids, up to 1,000 per run, and each video gets its own 2,000 row budget.

What the output looks like

Two rows, trimmed, from our run on October 7, 2026 on Rick Astley’s Never Gonna Give You Up video. That run used maxComments 20 and maxRepliesPerComment 5. The replier’s handle is masked here and their channel fields are dropped; your dataset has them in full.

[
  {
    "video_id": "dQw4w9WgXcQ",
    "video_channel_name": "Rick Astley",
    "comment_id": "Ugzge340dBgB75hWBm54AaABAg",
    "text": "can confirm: he never gave us up",
    "author_name": "@YouTube",
    "author_is_verified": true,
    "author_is_channel_owner": false,
    "likes": 324000,
    "likes_text": "324K",
    "reply_count": 962,
    "is_pinned": true,
    "is_hearted": true,
    "is_reply": false,
    "published_time_text": "1 year ago",
    "published_at": "2025-10-07T05:42:22.232Z",
    "position": 1,
    "sort_by": "top",
    "video_comment_count_text": "2.4M"
  },
  {
    "video_id": "dQw4w9WgXcQ",
    "comment_id": "Ugzge340dBgB75hWBm54AaABAg.AHE8_QAWJx9AHEB5iPJLDp",
    "text": "He's ingrained in our brains at this point. Such a devoted man.",
    "author_name": "@T*****************",
    "author_is_channel_owner": false,
    "likes": 748,
    "likes_text": "748",
    "reply_count": 0,
    "is_reply": true,
    "parent_comment_id": "Ugzge340dBgB75hWBm54AaABAg",
    "published_time_text": "1 year ago",
    "published_at": "2025-10-07T05:42:22.232Z",
    "position": 6,
    "sort_by": "top"
  }
]

The first row is the pinned comment from the @YouTube account: 324,000 likes, hearted by the channel, and 962 replies by reply_count. The second is one of the five replies the run read under it. Its parent_comment_id is the pinned comment’s id, which is also the part before the dot in its own comment_id. In that run the 20 rows for this video were 4 top comments and 16 replies, which shows how fast replies use up maxComments. Look at published_at too: YouTube only said “1 year ago”, so the date is the run time minus one year, right to the year and no closer.

Run it in Apify Console

  1. Open https://apify.com/themineworks/youtube-comments-scraper and click Try for free.
  2. In YouTube videos or Shorts, paste the video link, one per line if you have several.
  3. Set Max comments per video to 2000 and leave Sort comments by on Top comments.
  4. Switch on Include replies and set Max replies per comment to 50.
  5. Click Start and watch the rows arrive in the Output tab.
  6. When the run finishes, click Export and pick CSV, JSON or Excel.

The Console export is flat, but because every reply already sits under its parent, sorting by position keeps the threads together. The script below adds explicit thread columns.

Run it from Python

pip install apify-client, then:

import csv
from collections import defaultdict
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("themineworks/youtube-comments-scraper").call(run_input={
    "videos": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"],
    "maxComments": 2000,
    "sortBy": "top",
    "includeReplies": True,
    "maxRepliesPerComment": 50,
})

tops, replies = [], defaultdict(list)
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("_type") == "status":
        # free row for a video with comments off, a private video or a bad link
        print("skipped:", item.get("input"), item.get("status"), item.get("note"))
        continue
    if item["is_reply"]:
        replies[item["parent_comment_id"]].append(item)
    else:
        tops.append(item)

fields = ["video_id", "thread_id", "depth", "comment_id", "author_name", "text",
          "likes", "reply_count", "is_pinned", "is_hearted",
          "author_is_channel_owner", "published_time_text", "published_at",
          "comment_url"]

with open("comments.csv", "w", newline="", encoding="utf-8-sig") as f:
    writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    writer.writeheader()
    for top in tops:
        writer.writerow({**top, "thread_id": top["comment_id"], "depth": 0})
        for reply in replies.get(top["comment_id"], []):
            writer.writerow({**reply, "thread_id": top["comment_id"], "depth": 1})

read = sum(len(r) for r in replies.values())
print(f"{len(tops)} threads and {read} replies written to comments.csv")

Each row gets a thread_id equal to its top comment’s id and a depth of 0 or 1. A pivot table, or a groupby in pandas, can then count replies per thread, sum likes per thread, or put each top comment beside its most liked reply. Comparing reply_count with the replies you actually read tells you which threads were cut short by maxRepliesPerComment.

Refresh the export on a schedule

For a video you keep studying, such as a launch video in its first month, save the input as a task in Apify Console, then open Schedules, create a schedule with a cron expression such as 0 7 * * 1 (Mondays at 07:00) and pick the task. Each run reads the comments from scratch. The actor does not remember earlier runs and has no option for only new comments, so every weekly file is a full snapshot and every row is charged again. If you only want what is new, the monitoring guide linked below dedupes on comment_id. To send rows on, Apify’s Google Sheets integration, a webhook on a successful run, or Make, Zapier and n8n all work with this actor.

What it costs

$0.20 per 1,000 comment rows on the Bronze plan, and the same $0.20 on Silver, Gold and the higher plans, plus a flat $0.005 start fee per run. A reply costs the same as a top comment. The 2,000 row export above costs $0.40 plus $0.005. Ten videos at 2,000 rows each in one run come to 20,000 rows: $4.00 plus a single $0.005 start fee. Status rows for unreadable videos and repeated comments are never charged.

Limits worth knowing

  • Up to 20,000 rows per video. A big video’s total (2.4M on Rick Astley’s) is out of reach; you get the first rows in the order you chose.
  • Like counts above 1,000 are rounded by YouTube, so 324K becomes 324000.
  • published_at is worked out from relative text like “1 year ago”, so it is as exact as the unit YouTube shows: minutes for new comments, a year for old ones.
  • Replies come 10 to 50 per request, so threads are slower per row than top comments. At about 1.5 seconds per request, a 20,000 row video works out to roughly 25 minutes; we have not timed one yet.
  • Public videos only. Comments turned off, private or deleted videos return a free status row with the reason.
Related Actor

Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.

Frequently asked questions

Why does my CSV have fewer rows than the comment count under the video? +

The total YouTube shows under a video counts replies too, and maxComments caps the rows for each video at your setting, up to 20,000. Turn on replies and raise maxComments to get closer to the full count.

Do replies count toward maxComments? +

Yes. Top comments and replies share one budget per video, so a setting of 2,000 with replies on returns 2,000 rows in total, not 2,000 top comments plus their replies.

Are like counts exact? +

Below 1,000 they are. Above that YouTube only shows a rounded figure such as 324K, so likes holds 324000 and likes_text keeps the text as YouTube showed it.

How do I open the CSV in Excel without broken emoji? +

Write the file with the utf-8-sig encoding, as the script in this guide does. Excel then reads it as UTF-8, and emoji and accented letters display correctly.

Apify Store

Find a ready-made scraper for your job

Apify has over 80,000 scrapers and automations, ours included. Start free with $5 of platform credit every month.