YouTube Comments Scraper - Comment Replies & Threads avatar

YouTube Comments Scraper - Comment Replies & Threads

Pricing

from $0.50 / 1,000 comment or reply scrapes

Go to Apify Store
YouTube Comments Scraper - Comment Replies & Threads

YouTube Comments Scraper - Comment Replies & Threads

YouTube comments scraper that returns every comment AND every reply, with parentCommentId on every reply row so full comment threads reconstruct from one flat dataset. No login, no Google API key, no cookie.

Pricing

from $0.50 / 1,000 comment or reply scrapes

Rating

0.0

(0)

Developer

Eimantas V

Eimantas V

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

YouTube Comments Scraper — Comment Replies & Threads

YouTube comments scraper that returns every comment and every reply, with parentCommentId on every reply row so full comment threads reconstruct from one flat dataset. No login, no Google API key, no cookie, no browser.

Most YouTube comment scrapers give you top-level comments and stop. The replies are where the argument actually happens — the creator answering a complaint, the correction that got 400 likes, the thread that turned into a support queue — and this Actor returns them as first-class rows at the same price, linked to their parent.

What you get

One row per comment and per reply. Fill rates below were measured across 325 real rows from 7 videos sampled from live search, not hand-picked:

FieldTypeFillNotes
commentIdstring100%Stable YouTube id
parentCommentIdstring | null100% of repliesnull on top-level comments
isReplyboolean100%
replyLevelinteger100%0 top-level, 1 reply
textstring100%Newlines preserved
authorDisplayNamestring | null100%The @handle
authorChannelIdstring | null100%UC…
authorChannelUrlstring | null100%
authorAvatarUrlstring | null100%
likeCountinteger | null100%Parsed from the accessibility string
replyCountinteger | null100% of top-levelnull on replies
publishedTimeTextstring | null100%Relative only — see below
authorIsVerifiedboolean | null2.5% true
authorIsChannelOwnerboolean | null0.8% true
isHeartedByCreatorboolean | null14.2% trueThe creator's ❤
isPinnedboolean | nulltop-level onlyMeasured on 2 of 6 videos
repliesTruncatedboolean | nulltop-level onlyTrue when the thread had more replies than you asked for
sortBy, videoId, videoUrl, scrapedAt100%

Rebuilding threads

Every reply carries its parent's id, so one group-by is enough:

const threads = new Map();
for (const row of rows.filter((r) => r.commentId)) {
if (row.isReply) (threads.get(row.parentCommentId) ?? []).push(row);
else threads.set(row.commentId, []);
}

Rows also arrive thread-adjacent — each parent is immediately followed by its own replies — so a CSV export is readable without any processing at all.

A per-video coverage row

Alongside the comments, each video gets one status row, and it is never charged:

FieldWhat it tells you
commentsEnabledfalse when comments are turned off — a real answer, not a failure
commentsFetched / repliesFetchedWhat was written
commentsScannedRows examined, which is the work done
pagesFetchedRequests spent
totalCommentsReportedWhat YouTube itself claims the video has, so you can see what you missed. It is YouTube's own displayed figure, which is rounded on large videos — a video showing "10M" reports 10000000, not an exact count. Treat it as a scale check, not a precise total
repliesTruncatedThreadsHow many threads had replies you did not take
stoppedOnexhausted, maxCommentsPerVideo, maxTotalComments, zeroNewIds, …
stoppedOnScanLimittrue when a work budget stopped it, not the corpus
zeroReasoncomments-disabled, no-comments-yet, or null

This row exists because an empty dataset cannot tell you the difference between this video has no comments and we only read the first page. Those are different answers and you are entitled to know which one you got.

Input

{
"videos": [
"https://www.youtube.com/watch?v=jNQXAC9IVRw",
"dQw4w9WgXcQ",
"https://youtu.be/9bZkp7q19f0"
],
"sortBy": "top",
"includeReplies": true,
"maxCommentsPerVideo": 100,
"maxRepliesPerThread": 50,
"maxTotalComments": 10000
}

watch?v=, youtu.be/, /shorts/, /live/, /embed/ and bare 11-character ids all work, and extra parameters are ignored. Shorts need no special handling — they are ordinary videos to this endpoint. You can also chain from another Actor's dataset with sourceDatasetId.

Comment order is top (YouTube's default) or newest. That option was verified to actually change the output rather than merely being accepted: id-diffing Top against Newest gave 4/10, 0/10 and 1/10 first-page overlap across three videos, and Newest returns strict recency. Newest costs one extra request per video, because its token only exists inside the first page's sort menu — and that discarded page is not billed to you.

Pricing

EventPriceWhen
comment (primary)$0.50–$1.00 per 1,000Per comment or reply row
video-processed$0.00005Once per video, including comments-off
apify-actor-start$0.00005Once per run

Replies cost the same as comments. Your whole bill is:

(videos × $0.00005) + (comment rows + reply rows) × row price + $0.00005

Never charged: status rows, error rows of any kind, retried blocked pages, and the discarded page when using newest. Full arithmetic, including the measured cost basis, is in docs/PRICING.md.

What this does NOT do

Stated plainly, because a listing that overpromises just collects one-star reviews:

  • No absolute timestamps. YouTube returns only relative text ("17 hours ago"). This Actor gives you that string and does not convert it to a date, because "3 weeks ago" has a multi-day error bar and a converted value would look exact while being wrong. If you need real timestamps, the official API has them and this does not.
  • No transcripts, captions or subtitles. That transport was measured RED — 0 of 20 videos, including 16 that demonstrably have captions. Any Actor promising it alongside comments is promising something this endpoint no longer serves.
  • No comment search or date filtering. The endpoint offers neither. Faking it by paginating everything and filtering client-side would bill you for volume you did not ask for.
  • No channel or playlist expansion. Pass video URLs. A channel URL is rejected with a message telling you so, rather than guessed at.
  • No sentiment scores or AI enrichment. You get the text; run your own model on it if you want that.
  • No reply nesting beyond one level. YouTube itself does not have it — replyLevel is only ever 0 or 1.
  • No private, deleted, or held-for-review comments. Only what a logged-out visitor can see.
  • Deep reply pagination is bounded. The default takes the first 50 replies per thread and at most 5 reply pages. When a thread has more, the parent row carries repliesTruncated: true and the status row counts it — you are told, every time.

Should you use the free official API instead?

Sometimes, yes, and here is the honest test. The YouTube Data API v3 is free, gives you absolute timestamps, and has a default quota of 10,000 units/day. Comment threads cost 1 unit per call at up to 100 per page, so if you only need top-level comments, are happy to set up a Google Cloud project, and stay inside that quota, use it and do not pay for this.

This Actor earns its price when: you want replies as well as comments and do not want to spend a second quota-unit budget on comments.list per thread; you do not want to manage an API key or a Cloud project at all; you want the run to tell you what it missed; or you are already orchestrating on Apify and want this to chain onto another Actor's output.

Saying this out loud filters out the buyer who would churn — which is the point.

How it works, and the honest part

Plain HTTP JSON against YouTube's own InnerTube endpoint (POST /youtubei/v1/next), the one youtube.com calls from your browser. No browser is launched, which is what makes it roughly two orders of magnitude cheaper per row than a Playwright crawler.

There is no API key, and that is measured rather than assumed: a deliberately bogus key returned HTTP 200 with 397,262 bytes of full data, and omitting the key entirely returned 459,946 bytes with a valid continuation token. So there is no credential here to expire, and nothing bound to any human account.

robots.txt

You should know this before you buy, so it is here rather than buried. https://www.youtube.com/robots.txt contains, under User-agent: *:

Disallow: /api/
Disallow: /comment
Disallow: /results
Disallow: /youtubei/

The endpoint this Actor uses, /youtubei/v1/next, is covered by that last line. /watch is not disallowed, and is the only YouTube path this Actor touches outside /youtubei/ — it is used solely to re-read a client version string if one is ever retired.

This Actor accesses only publicly visible comments, sends no credentials, and bypasses no authentication or access control. It does not touch private, deleted, or moderation-held comments. Whether that is acceptable for your purpose is a decision for you and your legal advisers, and it will depend on your jurisdiction and what you do with the data. It is not a decision this README can make for you, and anyone selling you a YouTube comment scraper who does not mention robots.txt at all is not telling you less than this — they are just not telling you.

Reliability

  • Rate: measured clean at 174.5 requests/minute with no delay and no rate limiting across 159 consecutive requests — the ceiling was never found. The default pacing is about a third of that, deliberately.
  • Blocked pages are detected by shape, not status. A garbage input returns HTTP 200 with a normal-looking page and no error object anywhere. The Actor keys on the two top-level fields a genuine response always carries, and never on the body size — the same stub measured 55,361 bytes one day and 14,538 another.
  • It fails loudly rather than quietly. A retired client version answers HTTP 404 with a ~264-byte body; the Actor rotates versions, then re-reads a live one from /watch, and if that also fails it errors out rather than reporting an empty success. Every video that cannot be scraped gets a typed error row in the dataset with a stable errorClass.
  • Two independent budgets. maxCommentsPerVideo bounds the output; maxPagesPerVideo bounds the work. Both are reported. This matters because one measured video handed back a valid continuation token on five consecutive pages while returning zero comments — the Actor terminates on zero new ids, never on a token being absent.

Proxy

Apify datacenter proxy, on by default. Residential is deliberately not offered at any price: it is billed per gigabyte, it would consume the margin this Actor is priced on, and the transport was measured not to need it.