YouTube Comments API — Comments & Replies with Likes avatar

YouTube Comments API — Comments & Replies with Likes

Pricing

from $0.21 / 1,000 comment returneds

Go to Apify Store
YouTube Comments API — Comments & Replies with Likes

YouTube Comments API — Comments & Replies with Likes

Scrape YouTube comments as data. One row per comment: text, author, like count, reply count, relative and approximate absolute time, pinned and hearted flags. Sort by top or newest, walk reply threads, cap per video. Residential proxy included, no API key, no login.

Pricing

from $0.21 / 1,000 comment returneds

Rating

0.0

(0)

Developer

Insight Solutions

Insight Solutions

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

12 hours ago

Last modified

Share

YouTube Comments API — Comments & Replies

Get a YouTube video's comment section as data. Give this Actor a list of YouTube links — or bare video IDs — and get back one row per comment: the text exactly as posted, who wrote it, how many likes and replies it has, when it went up, and whether the creator pinned or hearted it. Ask for replies and each thread comes back too, threaded in behind the comment it answers.

No API key. No login. No cookies to paste. No YouTube Data API quota. $0.35 per 1,000 comments, residential proxy included, videos with comments turned off are free, and a run that returns no comments costs nothing at all.

Try it in 30 seconds

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

One watch URL, one bare video ID — both work, and so do youtu.be share links, Shorts, embeds and live URLs.

What comes back

{
"ok": true,
"rowType": "comment", // "comment" | "reply" | "diagnostic"
"videoId": "iG9CE55wbtY",
"videoUrl": "https://www.youtube.com/watch?v=iG9CE55wbtY",
"videoTitle": "Do schools kill creativity? | Sir Ken Robinson | TED",
"commentId": "UgwtA0zH0G1Lmsp4l-Z4AaABAg",
"parentCommentId": null, // set on a reply, naming its thread
"text": "Loved this quote:\n\"If you're not prepared to be wrong, you'll never come up with anything original\"\n- Sir Ken Robinson",
"authorName": "@obalfaqih",
"authorChannelId": "UC0BI507DZAOVYLEJ0617xDQ",
"authorUrl": "https://www.youtube.com/@obalfaqih",
"authorAvatarUrl": "https://yt3.ggpht.com/k1P8GMwE…=s88-c-k-c0x00ffffff-no-rj",
"authorIsCreator": false, // written by the channel that uploaded the video
"authorIsVerified": false,
"authorIsArtist": false, // Official Artist Channel badge
"likeCount": 6100, // expanded from "6.1K" — approximate above 1,000
"likeCountText": "6.1K", // the string YouTube actually rendered
"replyCount": 26,
"publishedTimeText": "7 years ago", // the only timestamp YouTube publishes
"publishedAtApprox": "2019-09-09T05:53:05.487Z", // resolved against scrapedAt
"isEdited": false,
"isPinned": false,
"isHearted": false, // ❤ from the creator
"sortOrder": "top",
"position": 1, // 1-based, in the order this run walked the video
"totalCommentCount": 13021, // what the video has, not what this run returned
"error": null,
"errorType": null,
"scrapedAt": "2026-09-09T05:53:05.487Z",
"source": "youtube.com",
"sourceUrl": "https://www.youtube.com/watch?v=iG9CE55wbtY"
}

With "includeReplies": true, each comment is followed by its replies as rowType: "reply" rows carrying the same columns, with parentCommentId set and a commentId of the form <parent>.<reply>. Sorting by position reproduces the thread exactly as it was read.

Use cases

  • Audience research — what people actually say under your videos, or under a competitor's, with the like count as a rough vote on each opinion.
  • Sentiment and topic analysis — one flat array of text, ready to hand to a model. likeCount weights it; publishedAtApprox lets you slice it by period.
  • Creator and community reporting — filter on authorIsCreator to pull the uploader's own replies, or on isHearted and isPinned for what they chose to endorse.
  • Support and product feedback — the comments under a launch video are a bug tracker nobody filed.
  • Moderation and brand safety review — read a section end to end and route it wherever your review process lives.
  • Spam and bot-pattern detection — repeated text across channels, or a burst of newest comments from accounts with no handle, both show up in this shape.

How it works, and why it keeps working

YouTube's public Data API charges quota for comments, tops out at 100 per page, and needs a Google Cloud project and an API key. This Actor reads the same comment section the browser reads, through the private InnerTube next endpoint the YouTube web player itself calls.

That endpoint answers in a shape worth understanding, because it is the reason cheap scrapers get this wrong:

  1. Asking next for a video returns no comments — only a continuation token for the comment section.
  2. Posting that token back returns a page of twenty threads, each of which is almost empty: a bag of opaque keys.
  3. The comment text, author, like count and heart state arrive separately, in a flat frameworkUpdates entity batch keyed by those same keys.

So every page has to be stitched: index the entities, resolve each thread against them. Reply pages use a third shape again, and their "next page" token hides behind a "Show more replies" button rather than the usual continuation — miss that one and every long thread silently truncates at ten replies. This Actor reads both spellings.

StepRequestWhat it gets
1next with the video IDThe video title and the comment-section token — or "comments are turned off"
1bWatch page (ytInitialData), only if step 1 returns no sectionThe same token, obtained the slow way; survives some API-level refusals
2next with the tokenThe header (total count, sort menu) and page one, twenty comments plus the next token
3…next with each next tokenTwenty more each time, until your cap, your budget, or the end of the section
Rnext with a thread's replies tokenOne thread's replies, when includeReplies is on

Underneath: Apify residential proxy, one pinned session per parallel worker. When YouTube refuses an exit IP — HTTP 429, HTTP 403, an empty body, or a body that is not JSON — that session is retired and the same page is asked for once more from a different residential address. Retrying on an address that was just refused only deepens the block, so it is never done. If the second address is refused too, the walk stops, keeps every comment it already delivered, and files one free blocked row saying where it stopped.

Pages of the same video are spaced 300–700 ms apart. Nothing forces that; it is the difference between reading a comment section and hammering one.

How it compares

  • No API key, no quota. The YouTube Data API needs a Google Cloud project and spends quota per call. This needs a video URL.
  • Replies are real rows, not a nested blob. One row per reply, with parentCommentId and position, so a spreadsheet or a SQL table can hold the thread without a JSON parser.
  • The counts are honest about their precision. likeCount is the integer you want; likeCountText is the string YouTube actually published, so you can see when "6.1K" is all the precision that exists.
  • Residential proxy is in the price. YouTube blocks datacenter IPs wholesale, so a comment scraper without residential egress does not work from a cloud host at all. There is no separate proxy line on your bill for this Actor.
  • Failures are free and legible. Comments turned off, a removed video, a bad ID or a block produce a diagnostic row with an errorType you can branch on — and no charge. A run that returns nothing at all finishes FAILED with the reason in its status message, never a green run containing an apology.
  • A partial walk is kept, not thrown away. Hit maxRunSecs or your charge ceiling on page 40 and you keep pages 1–39.

Input reference

FieldTypeDefaultWhat it does
videoUrls (required)array of stringsprefilled with one videoWatch URLs, youtu.be links, Shorts, embed or live URLs, or bare 11-character video IDs. Duplicates are read, and billed, once
maxCommentsPerVideointeger100Top-level comments per video. YouTube serves twenty at a time, so the walk stops on the page that reaches your number. Replies do not count towards it. 0 = every comment the video has
sorttop | newesttopYouTube's own two orders. newest costs one extra request per video — see the FAQ
includeRepliesbooleanfalseReturn each thread's replies as their own rows, threaded in behind the comment
maxRepliesPerCommentinteger50Cap per thread, when replies are on. 0 = the whole thread
includeAuthorDetailsbooleantrueFill in authorChannelId, authorUrl and authorAvatarUrl. Off keeps the display name and the badges
maxConcurrencyinteger3Videos in parallel. Each worker keeps its own proxy session. Pages within one video cannot be parallelised
maxRunSecsinteger240Whole-run wall-clock budget. When it runs out the Actor keeps what it has and files a free diagnostic row for each video it never reached
proxyConfigurationobjectApify residentialLeave it alone. Clearing it sends requests from the run's datacenter address, which YouTube will block

Output reference

Every row carries the same keys. ok: true is a comment or a reply; ok: false is a free diagnostic row.

FieldWhat it is
rowTypecomment, reply or diagnostic
videoId, videoUrl, input, videoTitleThe video, its canonical watch URL, the entry you supplied, and the title
commentId, parentCommentIdYouTube's IDs. A reply's ID is <parent>.<reply>
textThe comment body exactly as posted — newlines and emoji kept, nothing trimmed
authorNameDisplay name, which YouTube now renders as @handle
authorChannelId, authorUrl, authorAvatarUrlThe author's identity, when includeAuthorDetails is on
authorIsCreator, authorIsVerified, authorIsArtistUploader, verified badge, Official Artist Channel badge
likeCount, likeCountTextLikes as an integer, and as YouTube rendered them
replyCountHow many replies the comment has, whether or not you asked for them
publishedTimeText, publishedAtApprox, isEditedWhen it was posted, both ways, and whether it was edited since
isPinned, isHeartedPinned to the top by the uploader; hearted by the uploader
sortOrder, positionWhich order was walked, and where this row fell in it
totalCommentCountHow many comments the video has in total
ok, error, errorTypeWhether this row is a comment, and if not, why not
scrapedAt, source, sourceUrlWhen, and from where

errorType on a diagnostic row is one of:

ValueMeaningCharged?
comments-disabledThe uploader turned comments off for this videoNo
no-commentsThe section exists and is emptyNo
unavailablePrivate, removed, region-blocked, or a page with no comment sectionNo
blockedYouTube refused our requests from two different residential exits. Comments already returned for that video are keptNo
invalid-idThe entry was not a YouTube video — a channel, a playlist, or something elseNo
timeoutThe run's maxRunSecs budget ran out before this video was reachedNo

Pricing

$0.35 per 1,000 comments. Pay-per-event, with the residential proxy already inside that number — there is no separate proxy line on your bill for this Actor.

EventWhat triggers itFREEStarterScaleBusiness
Comment returned (primary)One comment or reply row written to your dataset$0.00035$0.00035$0.00028$0.00021
Run startedOnce per run, after the first comment$0.001$0.001$0.001$0.001

Worked example. 50 videos at 100 comments each, of which 2 videos have comments turned off:

  • 48 videos × 100 comments × $0.00035 = $1.68
  • 1 run start = $0.001
  • 2 videos with comments off = $0.00
  • Total: $1.681

What you are never charged for: a video with comments turned off, an empty comment section, a private or deleted video, an entry that was not a video, a video the run never reached before maxRunSecs, or a page YouTube blocked. If a whole run comes back empty it finishes FAILED and bills nothing at all, start fee included.

Set ACTOR_MAX_TOTAL_CHARGE_USD on a run and the Actor stops walking once the ceiling is in sight, rather than handing you rows it cannot bill or billing you for rows it cannot hand over. It finishes SUCCEEDED with the ceiling named in its status message, and everything already delivered is yours.

Limits, and the ones that might bite

Like counts above 1,000 are approximate. YouTube does not publish an exact like count for a comment anywhere — its own interface shows "6.1K", and that abbreviated string is the entire truth available over any interface, this Actor's included. likeCount expands it to 6100 so the column is sortable; likeCountText keeps "6.1K" so you can see the precision you have. Below 1,000 the number is exact.

Comment timestamps are relative, not absolute. Same reason: YouTube renders "7 years ago" and publishes nothing more precise. publishedAtApprox resolves that phrase against scrapedAt, stepping the calendar for months and years rather than multiplying by an average length — but "1 year ago" still covers twelve months of possible dates. Use it to sort and bucket, not to timestamp an event. publishedTimeText is the raw phrase, and it is the honest one.

Comments you can only see signed in. Held-for-review comments, comments hidden by a moderation filter, and members-only sections need an account. This Actor does not log in, does not accept cookies and does not take a session token, and it never will — that is a deliberate line, not a missing feature.

Reply depth. YouTube's comment tree is two levels deep. A reply to a reply is filed by YouTube itself under the same top-level comment, so parentCommentId always names a top-level comment and there is no third level to miss.

Live streams and premieres. A live chat is not a comment section and this Actor does not read it. A stream that has ended and kept its comments reads normally.

YouTube may change the format. This reads a private endpoint that YouTube changes without notice — that is true of every tool that reads YouTube comments, including the ones that do not say so. When a shape changes, rows stop arriving and you get free blocked or unavailable diagnostic rows rather than quietly wrong data, and a run that returns nothing bills nothing.

Rate and reliability. Requests go out through residential addresses with per-worker sessions, one rotation per block, and a 300–700 ms pause between pages of the same video. Three videos in parallel is the default because it is where throughput and block rate balance; raising maxConcurrency speeds a long list up and makes blocks more likely.

Use it from an AI agent, or from code

One JSON object in, one flat array out — the shape agent runtimes want. The Actor runs with limited permissions, uses pay-per-event pricing and never enters Standby, so it works over the Apify MCP server and with x402 agentic payments. The Integrations tab pushes results to Slack, a webhook, Zapier, Make, Google Sheets, Snowflake or BigQuery.

curl -X POST "https://api.apify.com/v2/acts/insight.solutions~youtube-comments-api/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"videoUrls":["iG9CE55wbtY"],"maxCommentsPerVideo":50,"sort":"newest"}'
# pip install apify-client
from apify_client import ApifyClient
client = ApifyClient("<APIFY_TOKEN>")
run = client.actor("insight.solutions/youtube-comments-api").call(run_input={
"videoUrls": ["https://www.youtube.com/watch?v=iG9CE55wbtY"],
"maxCommentsPerVideo": 200,
"sort": "top",
"includeReplies": True,
"maxRepliesPerComment": 20,
})
for row in client.dataset(run["defaultDatasetId"]).iterate_items():
if not row.get("ok"):
print("skipped:", row["input"], row["errorType"])
continue
indent = " " if row["rowType"] == "reply" else ""
print(f'{indent}{row["authorName"]} ({row["likeCount"]} likes): {row["text"][:80]}')

Turn includeAuthorDetails off when you only need the text and the numbers — the payload shrinks and the price is the same.

FAQ

Why are like counts approximate? Because YouTube's own comment section is. It renders "6.1K" and publishes no exact number behind it, for any client. Anything from 6,050 to 6,149 arrives as 6100, and likeCountText keeps the original string so the loss is visible rather than hidden. Under 1,000 the count is exact.

Why are the dates relative? Same reason. A comment carries "7 years ago" and nothing else — there is no exact posting time in the response to read. publishedAtApprox turns the phrase into a sortable instant measured back from scrapedAt; treat it as a bucket, not a timestamp.

How many comments can I get from one video? As many as it has. Set maxCommentsPerVideo: 0 and raise maxRunSecs. A video with 13,000 comments is 650 sequential page requests with a pause between each — plan for minutes, not seconds, and bound it with ACTOR_MAX_TOTAL_CHARGE_USD if you are not sure what you are asking for.

Why does newest cost an extra request? Because "newest" is not a parameter — it is a different continuation token, and the only place that token exists is the sort menu in the header of page one. So a newest walk fetches page one under the default order, reads the token, and starts again. One extra request per video, and there is no cheaper way to get it.

Are replies charged? Yes, at the same rate as comments — a reply is a row. That is why includeReplies is off by default and maxRepliesPerComment exists.

Do I need my own proxy or an API key? Neither. Apify residential proxy is configured by default and its cost is inside the per-comment price. No Google Cloud project, no YouTube Data API key, no quota.

Will it handle a playlist or a whole channel? Not yet — this Actor takes videos. Feed it a list of video URLs from wherever your list comes from.

What happens if one video fails? The others still run. The failed one produces a free diagnostic row and the run finishes SUCCEEDED. If every video fails, the run finishes FAILED and you are billed nothing at all.

Is the data fresh? Live. Every run reads YouTube at that moment; nothing is cached.

  • Public videos only. Every source is a public watch page. The Actor never logs in, never accepts cookies or session tokens, never takes an API key belonging to anyone else, and never touches private, members-only or held-for-review content.
  • Comments are personal data in most jurisdictions. A comment carries a display name, a channel ID and an avatar, and under the GDPR and similar laws that is personal data about an identifiable person. You are the controller of whatever you collect: have a lawful basis, keep only what you need, honour deletion requests, and remember that a comment deleted on YouTube stays in your dataset until you remove it. includeAuthorDetails: false is there so that analysis which does not need identities does not carry them.
  • Comments are their authors' words. Republishing them, or training on them, is your call and your responsibility, subject to YouTube's terms and to the law where you operate. Aggregation, sentiment analysis and quotation are the ordinary uses and are what this is built for.
  • Not affiliated with YouTube, Google LLC, or with any channel, creator or commenter whose content you retrieve. All product names and trademarks belong to their respective owners and are used only to describe which public endpoints this Actor reads.

Our other Actors

Every Insight Solutions Actor is pay-per-result with no browser, no login and no API key, and every one of them returns free diagnostic rows instead of billing for failures. Prices are per 1,000 results.

Video, audio & social

News, documents & the web

Business, finance & jobs

Apps & games