YouTube Comments Scraper — Replies, Likes & Channel Videos
Pricing
from $0.60 / 1,000 comments
YouTube Comments Scraper — Replies, Likes & Channel Videos
Paste YouTube video, Shorts or channel links and get the comments as rows: text, author, likes, reply count, pinned and creator-heart flags, plus the replies underneath at the same price. Top or newest order, exact per-video limit, no start fee. Comments off is an uncharged row, never a charge.
Pricing
from $0.60 / 1,000 comments
Rating
0.0
(0)
Developer
Steadyfetch Team
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Click Start with nothing set and the run returns a small built-in sample: three rows cut from a public video's own comment page, one of them a reply folded under its parent, so you can read the columns before you plan a big run. It is built in rather than fetched — nothing is collected and nothing is charged. The comment text and the video are as they were published; the commenter names in the sample are anonymised, and the rows say so. No login, no cookies, nothing to configure, no start fee and no result fee.
Using an AI agent? Pin this actor in Apify's MCP server with one link: https://mcp.apify.com?tools=steadyfetch/youtube-comments-scraper
- Actor id:
steadyfetch/youtube-comments-scraper - Input:
{ "videoUrls": ["aqz-KE-bpKQ"] }— the one field you have to set. A bare 11-character video id as shown, or the watch link,youtu.belink, Shorts, live or embed URL you copied. Add"resultsLimit": 200for an exact per-video limit, and"channels": ["@NASA"]to take a channel's newest uploads instead of naming videos one by one. - Cap the bill: set
maxTotalChargeUsdon the run (a run option, not Actor input), e.g.0.50— the run stops when it reaches it.
Unofficial. Not affiliated with, endorsed by, or sponsored by YouTube or Google LLC.
Paste a channel link and it works. A handle, /@handle, /channel/UC…, /c/… or /user/… returns the comments under that channel's newest uploads, videosPerChannel of them (default 5). No other comments scraper on this store takes a channel link at all.
What one comment costs
One charged event, comment, and it lands only on a comment that was actually delivered. A reply is a comment at the same price, folded under its parent — never a second event and never a second purchase.
| Apify plan | Per comment | Per 1,000 comments |
|---|---|---|
| Apify free plan | $0.0024 | $2.40 |
| Bronze | $0.0014 | $1.40 |
| Silver | $0.0009 | $0.90 |
| Gold and above | $0.0006 | $0.60 per 1,000 comments |
The headline on this listing reads from $0.60 per 1,000 comments, and the table above is what that means on your own plan — so you can work out the bill before you start rather than after. Platform usage is included in that price: there is no separate compute bill on top, no start fee, and no charge for a comment that was not delivered. Set maxTotalChargeUsd on the run and it is a hard ceiling — the run stops cleanly under it and the last row says what is left.
What you get
One row per comment. A reply is a row like any other, with isReply and parentCommentId saying where it belongs, so a thread reads in order.
| Column | What it holds |
|---|---|
commentId | the comment's own stable YouTube id |
text | what they wrote |
authorName · authorChannelId · authorUrl | who wrote it |
authorIsVerified · authorIsCreator · authorIsArtist | YouTube's own badges on that author, including whether it is the uploader replying |
likeCount · likeCountText · likeCountIsApproximate | the parsed number, the label YouTube actually published, and whether that label was rounded |
replyCount | how many replies the comment has |
publishedText · publishedAt · publishedAtPrecision | the label YouTube published, the date derived from it, and how precise that derivation really is |
isPinned · heartedByCreator | pinned by the uploader, and hearted by them |
isReply · parentCommentId | whether it is a reply, and to which comment |
videoId · videoUrl · videoTitle · channelName · channelId | the video it came from and who uploaded it |
sort · source | which order this row was collected in, and whether the video came from your list or from a channel |
charged · chargedEvents · status · statusReason | whether this row was billed, which event billed it, and why — so a single row can be checked against your invoice, and the whole dataset reconciles it |
Your limit is exact and it is per video. Set resultsLimit to 200 and you get 200 from each link, replies counted inside that number. If your input carries maxComments, commentsPerVideo, limit or count instead — the names other comment scrapers use — it is read as resultsLimit and one uncharged note row says so. maxItems (default 1,000) is the ceiling on the whole run.
How deep this goes
This is the one thing to know before you ask for a big number, and it is the question this listing exists to answer honestly. There are two separate limits on it and they are worth keeping apart: how deep YouTube will serve, which is the same for everyone, and how deep one run of yours goes, which is set by your own settings.
What YouTube serves
YouTube itself stops serving comment pages, long before a big video runs out of comments, and how far it goes depends on the order you ask for. Walked to exhaustion twice, a day apart, on 2026-09-11 and 2026-09-12:
| Order | Where YouTube stopped serving |
|---|---|
sort: top (the default) | about 1,200 comments per video, whatever the video's size — around 1,220 on a video with 2.46 million comments, and around 1,130–1,140 on one with 5,171 |
sort: newest | still serving at 3,000 comments when we stopped walking, with more to come — deeper than Top, and we have not found the end of it |
That ceiling is YouTube's, not ours, and no setting on this actor moves it. Every walk ended cleanly — YouTube simply stopped handing out the next page — rather than being blocked.
And it is approximate, which is why this table says "around". We walked the same video on the same order a day apart and YouTube stopped at 1,130 comments one day and 1,137 the next, both times on a clean end. It is YouTube deciding when to stop serving, not a published limit, so the number moves a little between runs. Plan against the range, not against a figure — and when your own run lands a few comments either side of it, that is the surface, not a fault in the run.
So: if you want depth, ask for sort: newest. If you want the comments people actually engaged with, sort: top is the right order and around 1,200 is what exists. When the pages run out before your resultsLimit is reached, the run leaves an uncharged depth_reached row saying how many comments that order served for that video and pointing at sort: newest as the deeper door. You are never billed for the gap, and you are never left to work out that there was one.
Two of our own bounds end a walk early too, and the row says so instead of blaming YouTube. One video will not walk past 400 comment pages, so it can never spend a whole run on its own; and a re-run whose pages hold only comments your account already has stops after ten such pages rather than keep buying pages that add nothing (a token that cannot open key-value stores cannot see what you already have — the run says so and collects as usual). Both end in the same uncharged depth_reached row, and in both cases the row names the bound as ours and the setting that opens it — Include comments you already have for the second, the other comment order for either.
What a default run returns
Those are ceilings on what you CAN reach, not on what a default run brings back — your own settings stop it first, and on purpose. A run that you start without changing anything ends at whichever of these three comes first, and they are listed in the order they usually bind:
resultsLimit, which defaults to 100. This one is per video, and it is what stops an ordinary run: paste one link, press Start, and you get 100 comments from it — replies counted inside that number — not the thousand-odd the table above says are there.maxItems, which defaults to 1,000. A ceiling on the whole run, every video and every reply counted together. This is the one that binds when you paste a long list of links or point the run at a channel.maxRunSeconds, which defaults to 600. Collecting takes time, and a deep walk into a busy video can reach the clock before it reaches the rows.
Whichever one stops it says so on an uncharged row naming the setting, so you never have to guess which of your own limits bound the run. To go deeper on one video, raise resultsLimit; to collect from more videos in a run, raise maxItems too; raise maxRunSeconds only if the clock is what the row actually named. Keep maxTotalChargeUsd on the run as your ceiling on the bill — and note what the depth you are asking for costs: a thousand comments is $2.40 on the Apify free plan and $0.60 on Gold, so the defaults are deliberately modest rather than generous. The numbers in the table above are what is there to be collected once you have raised them.
Dates and likes
YouTube publishes relative labels, not timestamps. What the page carries is "1 year ago" or "16 minutes ago" — there is no exact date on the surface at all, for anyone. So the row ships three columns instead of one invented one:
publishedText— the label, verbatim, exactly as YouTube published it.publishedAt— a date derived from that label.publishedAtPrecision—minute,hour,day,week,monthoryear: how precise that derivation actually is.
A comment labelled "1 year ago" comes back with publishedAtPrecision: "year", never a millisecond stamp dressed up as a fact. Sort by publishedAt and it behaves; read it as an exact time and the precision column tells you not to.
Likes above 999 are rounded by YouTube — the page says "314K", not 314,271. The row carries likeCount (314000), likeCountText ("314K") and likeCountIsApproximate (true), so the rounding is named rather than hidden inside a number that looks exact.
When a video has no comments to give
Every one of these is still delivered as a row, labelled, with a sentence saying which it was — never a silent gap in your dataset, and none of them is charged.
| the row says | what happened |
|---|---|
comments_disabled | the uploader turned comments off for that video. We learn it from the video's own page, before any comment page is requested, so it costs one read and it costs you nothing |
no_comments | comments are on and nobody has left one. A definitive answer, not a failure, and never retried |
removed_or_unavailable | the video is gone |
private_or_members_only | the video is private, or behind a membership |
age_restricted · region_blocked | the video is gated to signed-in or to other regions |
channel_not_found · not_a_channel · channel_no_videos | the channel link did not resolve, or the channel has no uploads to read |
depth_reached | the walk ended before your limit — YouTube stopped serving pages, or one of our two bounds did. The row says which, and what to change |
skipped_budget · skipped_deadline | your own maxTotalChargeUsd or the run clock ended the collecting |
comments_unavailable | the read did not go through. Temporary, says nothing about your video, and a re-run is the fix |
input_error — and input_error.videoUrls, input_error.channels, input_error.startUrls, input_error.sort | something in that field could not be read. The row names the field, never what you typed |
"Comments are turned off" and "this video is gone" are two different answers, and this actor tells them apart. That distinction costs one extra read on our side and nothing at all on yours — it matters because a run that reports "not found" about a video you can watch is a bug report you would have to file.
How often this data changes
Comments keep arriving for days after a video goes up — the first hours are the busiest — and likes on a comment keep moving for weeks after the comment itself stops changing. A channel's newest uploads move every time it publishes. So the useful pattern here is not one run: it is the same links again tomorrow.
Re-running would otherwise re-buy the comments you already have, so it does not. Every run remembers what it delivered to your account, in the key-value store yt-comments-account in your own Apify account, and a later run skips those: not delivered again, not charged, and the slot goes to the next new comment instead — so you still get the number you asked for, as long as there are that many new ones to find. When a re-run meets ten pages running with nothing new on them, it stops there and says so on an uncharged row rather than buying deeper into comments you already own. That makes a daily or weekly watch cost only what is new. The memory is a key-value store in your own Apify account, so a run started with a scoped API token in restricted-access mode needs key-value store Read, Write and Create permission, or Actor runs set to Full access (Write is what lets the run remember what it delivered; without it every run pays again); without it the run says so on its status line and on an uncharged row of its own, and cannot skip what you already have. Switch Include comments you already have (includeSeen) on to get them back anyway, marked and still uncharged, and use resumeFromDatasetId to carry the same memory across accounts.
For a video older than a few weeks on sort: top, a second run buys almost nothing new — that one is worth reading once. For an upload from this week, or for a channel you are tracking, it is worth a schedule.
Limits, and the run's own manners
Your settings are never refused for being too big. Ask for more comments per video than YouTube will serve, more videos from a channel than the run can walk, or a shorter clock than the work needs, and the run still starts: it continues at the nearest limit and leaves one uncharged row saying what you asked for and what bound it. maxRunSeconds (default 600) is the clock, videosPerChannel (default 5) the channel door's depth, sort is top or newest and an order we do not know is refused on an uncharged row naming the two that work — never quietly swapped for something else.
A cost cap or a time limit ends the collecting, never the delivering. Comments already in hand are written out whatever stops the run, and the last row of every run is a receipt: delivered, how many were replies, asked for, what your account already had, what stopped it, and the charged totals — so an invoice reconciles from the dataset itself.
Only public videos are read. This actor may stop working if YouTube changes how comments are served; if it does, the rows say so and nothing that failed is charged.
Related actors
- The spoken words inside one video: YouTube Transcript API & Scraper — YouTube Subtitles, Speech AI
- Every video on a channel, as text: YouTube Channel Videos Transcript Scraper — Bulk Video to Text
Something not right?
Open the Issues tab on this actor with your run ID and the input you used, and it gets looked at — usually within a couple of hours.
If it earned its keep, a rating helps other buyers find it, and saving the actor keeps it one click away.