YouTube Comments Scraper: Replies, Shorts & Video Comments avatar

YouTube Comments Scraper: Replies, Shorts & Video Comments

Pricing

from $0.90 / 1,000 comments

Go to Apify Store
YouTube Comments Scraper: Replies, Shorts & Video Comments

YouTube Comments Scraper: Replies, Shorts & Video Comments

Scrape YouTube comments and replies, no login or API key: a YouTube comment scraper and comments extractor for video, Shorts and community post URLs, a channel, playlist or search. Full text, author, likes, reply count, date, pinned and hearted flags, the exact comment count. Newest or top.

Pricing

from $0.90 / 1,000 comments

Rating

0.0

(0)

Developer

Hyperbach

Hyperbach

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

4 days ago

Last modified

Share

YouTube comments as data, logged out: video, Shorts and community-post URLs in — or a channel's latest videos, a playlist or a keyword search — and one row per comment out, with its replies on request. Each comment has its whole text with every link unwrapped, its @mentions and time links, the author's @handle, channel id and avatar, the creator, verified and artist flags, the likes (exact below 1,000, YouTube's own rounding past it, never invented), the reply count, the published time as YouTube writes it with an approximate date and its range, and the edited, pinned and hearted-by-creator flags read from that comment itself. Every row carries its video's title, channel and exact comment count ("2,460,185", where the page says "2.4M"). Every video and post gets a free summary row: what was delivered, the coverage against the count, and a verdict — COMPLETE, TRUNCATED (YouTube's own list ended early), CAPPED (your limit) or PARTIAL. Comments turned off, a missing video, a premiere, a members-only video, a live stream: free rows that say which. Replies are capped and billed apart from comments. No login, no YouTube account, no cookie, no API key, proxies handled for you.

Why this YouTube comments scraper

  • Every comment of a video, with a proof. Newest first walks YouTube's whole list to its own end: on our test video 247 comments and 113 replies, 360 of the 362 its header counts (YouTube counts held or removed comments it does not show). The video's summary row says how many were delivered against the exact count and gives a verdict, so a short dataset is never silent. YouTube's Top order is a ranking that ends at about 1,000–1,200 comments on a famous video (1,199 of 2.46 million on our test): the row says TRUNCATED with the count, and Newest reads the rest.
  • Pinned and hearted, right. The pinned flag is YouTube's own pin on that comment and the heart is read from that comment's own toolbar, never guessed from the page: on our test video the creator's pinned comment and the hearted ones came out as YouTube shows them, where 6 and 7 rivals got these two flags wrong on the same video.
  • Likes and counts that do not lie. Below 1,000 the likes and reply counts are exact; past it YouTube itself only shows a rounded text ("1.6K", "568K"), so the row gives that number, the text beside it and likes_exact false — never digits YouTube does not show. The comment count is the exact one from YouTube's comments header, on every row.
  • Replies apart, capped apart. Replies come on request (includeReplies), each its own row with its parent's id, up to maxRepliesPerComment; maxComments counts comments only. A thread is read 181–191 replies a call, so a 963-reply thread takes 5 calls.
  • A date cut-off that holds. oldestCommentDate (a date or "7 days") reads newest first and stops at the first comment older than the date; the pinned comment at the top of the list is not taken as the newest. YouTube shows a comment's age in words ("3 weeks ago"), and every row carries the text, an approximate date and the range the text allows.
  • More than video URLs. Shorts, live and embed links, community posts, a channel's latest videos, Shorts or posts, a playlist, or the top videos of a keyword search from the country you pick — each video's comments with the same columns, and one row and one charge per comment whatever inputs overlap.
  • Fast, plain HTTP. YouTube's own JSON answers, masked to the fields a row needs: no browser, no cookie, no API key. On our test one video's 360 comments and replies took 6 seconds, and the 300 newest of a 2.46-million-comment video with two more videos beside them 7 seconds.

Who it's for

  • Marketers and brand teams — what viewers say under a campaign's or a competitor's videos, newest first with a date cut-off, the creator's pinned and hearted picks marked, on a schedule with onlyNew.
  • Researchers and analysts — every comment of a video with its replies and the coverage against YouTube's exact count, for sentiment, topic or discourse analysis; a keyword search's top videos read from a chosen country.
  • Creators and channel managers — a channel's latest videos, Shorts or community posts and their comments, with likes, reply counts and the questions viewers ask; keyword and likes filters to find them.
  • Developers and data teams — typed rows keyed by comment_id with parent_id for the thread, the video's exact comment count on each, a free summary row per video, a run summary to check a pipeline against.

Quick start

The 300 newest comments of a video (the leader's default job)

{
"startUrls": [
"https://www.youtube.com/watch?v=dQw4w9WgXcQ"
],
"maxComments": 300
}

Every comment and reply of a video

{
"startUrls": [
"https://www.youtube.com/watch?v=krsuaUp__pM"
],
"maxComments": 0,
"includeReplies": true
}

The top 50 comments of a Short and a community post

{
"startUrls": [
"https://www.youtube.com/shorts/cEXeBA42_3c",
"https://www.youtube.com/post/Ugkx2lkCYp85jak0n5z7-bq_4ug2Rntk9iWT"
],
"maxComments": 50,
"sortCommentsBy": "TOP_COMMENTS"
}

The last 30 days of comments on a channel's 5 latest videos

{
"startUrls": [
"https://www.youtube.com/@hello_interview"
],
"maxVideos": 5,
"maxComments": 0,
"oldestCommentDate": "30 days"
}

Comments that mention a word, under a keyword search's top videos

{
"searchQueries": [
"web scraping"
],
"maxVideos": 3,
"maxComments": 0,
"includeKeywords": [
"python"
]
}

Output

One row per comment and one per reply (and one free summary row per video, post, channel, playlist or search):

fieldmeaning
typecomment (a top-level comment, charged), reply (a reply to a comment, charged as a reply), or a free summary row: video or post (one per video or community post: its status, what was read, the exact count, the verdict), list (one per channel, playlist or search input), input (a value that names nothing).
statusA comment or reply: ok. A video or post row: ok (its comments were read; see verdict), comments_off (the creator turned them off), not_found (no video with that id: two reads agree), upcoming (a premiere or stream not started), members_only, live (a live stream: its chat is not comments), no_comments, failed (YouTube served the video and no comments list, or refused it), not_read (maxItems, the charge limit or the time limit came first). A list row: ok, not_found, failed, not_read. An input row: invalid.
comment_idYouTube's comment id (a reply's id is its parent's id, a dot and its own); the de-duplication key: one row and one charge per comment a run.
comment_urlThe comment on YouTube: the video's URL with &lc=<comment id> (YouTube opens the page with this comment on top), or the post's ?lc=.
parent_idOn a reply: the id of the comment it answers; null on a comment.
reply_level0 for a comment, 1 for a reply (YouTube nests one level).
position1-based place in the video's list in the order read (sort); a reply's place in its thread.
textThe whole comment as YouTube shows it, emoji and line breaks kept.
author_handleThe author's @handle (YouTube shows no other name on a comment).
author_channel_idThe author's channel id (UC…).
author_channel_urlThe author's channel on YouTube.
author_avatar_urlThe author's avatar image URL (not downloaded).
author_is_creatorTrue when the video's (or post's) own channel wrote it, read from YouTube's own flag on the comment.
author_is_verifiedTrue when the author has YouTube's verified badge.
author_is_artistTrue when the author is an official artist channel.
likesThe likes as a number: exact below 1,000; past it YouTube shows only a rounded text ("1.6K"), read as 1,600 with likes_exact false. 0 when YouTube shows none.
likes_textThe likes exactly as YouTube writes them ("285", "1.6K", "568K"); null for 0.
likes_exactFalse when YouTube rounded the likes (1,000 and more).
reply_countThe replies YouTube says the comment has: exact below 1,000, rounded past it ("1K").
reply_count_textThe reply count as YouTube writes it; null for none.
reply_count_exactFalse when YouTube rounded the reply count.
published_textWhen it was posted, as YouTube writes it logged out ("3 weeks ago", "1 year ago (edited)"); YouTube serves no exact time without an account.
published_at_approxThe approximate time from the text and the read time: the read time minus the stated age ("3 weeks ago" = 21 days before the read); null when the text is not in English.
published_afterThe earliest time the text allows ("3 weeks ago": 28 days before the read).
published_beforeThe latest time the text allows ("3 weeks ago": 21 days before the read).
published_at_is_estimatedTrue on every row with a computed date: it comes from YouTube's relative text, not from an exact time.
is_editedTrue when YouTube marks the comment "(edited)".
is_pinnedTrue on the comment the creator pinned (YouTube's own pin on that comment).
is_heartedTrue when the creator gave the comment a heart, read from that comment's own toolbar.
linksEvery link in the text, as its real URL (YouTube's redirect unwrapped).
mentionsEvery channel the text @mentions: {handle, channel_id, url}.
timestampsThe video times the text links to, in seconds (a "2:15" in a comment).
video_idThe video's 11-character id; null on a post's rows.
video_urlThe video on YouTube.
video_titleThe video's title as YouTube shows it.
video_channel_idThe channel that published the video (or the channel of a channel input).
video_channel_nameThe publishing channel's name.
video_published_textThe video's date as YouTube's watch page shows it ("Jun 16, 2024").
video_comment_countThe video's (or post's) exact comment count from YouTube's own comments header ("2,460,185 Comments"; the page rounds it to "2.4M"). It counts replies too.
post_idThe community post's id, on a post's rows.
post_urlThe community post on YouTube.
sortnewest or top: the order the comments were read in.
verdictOn a video or post row: COMPLETE (the list's end), TRUNCATED (YouTube's own list ended well before the count, as Top does on a famous video), CAPPED (maxComments, maxItems, the date cut-off or onlyNew came first), PARTIAL (stopped by an error or the time limit). On a list row: COMPLETE or CAPPED at maxVideos.
comments_readOn a video or post row: the comments delivered from it.
replies_readOn a video or post row: the replies delivered from it (null when Replies is off).
coverageOn a video or post row: (comments + replies delivered) ÷ the exact count.
pages_readOn a video or post row: the comment pages read (20 comments a page).
videos_foundOn a list row: the videos (and posts) the channel, playlist or search gave.
inputThe value you gave that led to this row.
input_typevideo, post, channel, playlist, search or invalid.
search_countryOn rows from a keyword search: the country the search was read from (YouTube's own answer names it; a search served from another country is asked again on a fresh exit, then a free failed row).
noteWhy, in words: the reason of a free row, what a walk did not read and why.
scraped_atWhen the row was read (UTC).

Example record:

{
"type": "comment",
"status": "ok",
"comment_id": "UgxwgNrhdItM14Pg_BF4AaABAg",
"comment_url": "https://www.youtube.com/watch?v=krsuaUp__pM&lc=UgxwgNrhdItM14Pg_BF4AaABAg",
"parent_id": null,
"reply_level": 0,
"position": 1,
"text": "I had to cut a ton to keep this short enough. The full written breakdown has the deep dives I couldn't fit (including the diagrams):\n→ https://hellointerview.com/youtube/web-crawler/pinned-comment\n\nAnd honestly, watching me solve it is way less useful than trying it yourself. You can practice the exact problem with instant feedback step-by-step here:\n→ https://hellointerview.com/practice/youtube",
"author_handle": "@hello_interview",
"author_channel_id": "UC3kf-QFT6FZzDk9JsPg8Svg",
"author_channel_url": "https://www.youtube.com/@hello_interview",
"author_avatar_url": "https://yt3.ggpht.com/EF0G_E_LsoqwayBpT8S4WheoMyMQHOhL6TMeM3sHZlew4Kmv7l7sTigvFv8MIudBSoOIbRyS=s88-c-k-c0x00ffffff-no-rj",
"author_is_creator": true,
"author_is_verified": false,
"author_is_artist": false,
"likes": 6,
"likes_text": "6",
"likes_exact": true,
"reply_count": 1,
"reply_count_text": "1",
"reply_count_exact": true,
"published_text": "1 year ago (edited)",
"published_at_approx": "2025-10-21T03:57:15Z",
"published_after": "2024-10-21T03:57:15Z",
"published_before": "2025-10-21T03:57:15Z",
"published_at_is_estimated": true,
"is_edited": true,
"is_pinned": true,
"is_hearted": false,
"links": [
"https://hellointerview.com/youtube/web-crawler/pinned-comment",
"https://hellointerview.com/practice/youtube"
],
"mentions": [],
"timestamps": [],
"video_id": "krsuaUp__pM",
"video_url": "https://www.youtube.com/watch?v=krsuaUp__pM",
"video_title": "Design a Web Crawler System Design Interview w/ a Ex-Meta Staff Engineer",
"video_channel_id": "UC3kf-QFT6FZzDk9JsPg8Svg",
"video_channel_name": "Hello Interview",
"video_published_text": "Jun 16, 2024",
"video_comment_count": 362,
"post_id": null,
"post_url": null,
"sort": "top",
"verdict": null,
"comments_read": null,
"replies_read": null,
"coverage": null,
"pages_read": null,
"videos_found": null,
"input": "https://www.youtube.com/watch?v=krsuaUp__pM",
"input_type": "video",
"search_country": null,
"note": null,
"scraped_at": "2026-10-21T03:57:15Z"
}

Pricing

Pay for the comments and replies a run delivers, plus one run start; no platform usage is billed to you. The row prices fall with your Apify plan.

eventunitFreeStarterScaleBusiness
Comment (comment)per 1,000$2.00$1.50$1.20$0.90
Reply (reply)per 1,000$2.00$1.50$1.20$0.90
Run start (start)per run$0.0015$0.0015$0.0015$0.0015

A comment is $2.00 per 1,000 on the Free plan ($0.002 a comment) and $1.50 on Starter; a reply, only when you switch includeReplies on, $2.00 per 1,000 on Free and $1.50 on Starter, capped apart from the comments by maxRepliesPerComment. Each run that delivers a comment or a reply also pays the run start, $0.0015 once; a run that delivers none pays nothing at all. So the 300 newest comments of a video cost $0.6015 on the Free plan and $0.4515 on Starter; every comment and reply of our 362-comment test video (247 comments, 113 replies) $0.7215 and $0.5415; 1,000 comments $2.0015 and $1.5015; several videos in one run share one start. Free: every summary row (a video's or a post's status, counts and verdict; a channel's, playlist's or search's list row), comments turned off, a missing, upcoming, members-only or live video, a value that names nothing, an input left unread at a cap or the time limit, a comment a keyword or likes filter leaves out, and a comment onlyNew skips. A comment is charged once a run whatever inputs overlap; a run that stops early (a timeout, an abort) is charged for the rows it delivered. maxItems counts the charged rows. Before the first request the log names the price of each row at your plan, the run start and, when a cap bounds the run (maxComments with maxRepliesPerComment when replies are on, maxVideos for a list, maxItems), the most it can cost, the cap filled with the dearer rows first. With maxComments 0 (every comment) and no maxItems a run has no bound but YouTube's own lists, which run from a few comments to millions: the log names YouTube's count for each video when its first page is read and what reading it costs (5 ordinary videos of one channel: 343 rows against their counted 344; a famous music video: 2.46 million). Set maxComments, maxItems or a charge limit for a lower maximum. Enterprise plans have their own rates — the Actor's Pricing tab shows the price for your plan.

Usage patterns

  • What to give — startUrls: videos and Shorts (watch, youtu.be, /shorts/, /live/, /embed/ links, or an 11-character id), community posts (/post/ URLs), channels (URL, @handle or UC… id), playlists (URL or PL… id), search URLs; the leader's {"url": …} objects work too. searchQueries: keywords, each the search for its top maxVideos videos. A value repeated is read once; a value that names nothing is a free invalid row.
  • How many comments — maxComments (100 by default; 0 = all) caps the comments of each video or post, in the order of sortCommentsBy. Replies are read with includeReplies and capped by maxRepliesPerComment (0 = all), apart from maxComments. maxItems caps the charged rows (comments and replies) of the whole run. A video's summary row says whether its list ended (COMPLETE), YouTube's own list ended well before the count (TRUNCATED: the Top order on a famous video) or a limit came first (CAPPED).
  • Newest or top — NEWEST_FIRST (the default) is YouTube's chronological list: it holds every comment the video shows, so it is the order for all comments, a date cut-off and onlyNew. TOP_COMMENTS is YouTube's ranking (the order the page shows first); on a famous video it ends at about 1,000–1,200 comments, and on a small video it can leave some out. The creator's pinned comment heads both lists; newest first it is delivered at its own date.
  • Channels, playlists and searches — A channel reads its maxVideos newest videos (10 by default), or its Shorts or both (channelTab), and with maxPosts its latest community posts; a channel URL that ends in /shorts, /streams or /posts reads that tab. A playlist reads its first maxVideos videos in its order, a keyword search its top maxVideos videos from searchCountry. Each list gets a free summary row with the videos it found; each video its own summary row.
  • Scheduled runs: only the new comments — Save a task with onlyNew (and a stateStoreName to name the memory) and schedule it: a comment an earlier run of that memory delivered is skipped free, and newest first a video's walk stops at the first comment delivered before. When more new comments came than maxComments, the oldest new ones are delivered first and the summary row says how many wait for the next run, so none is lost. The memory keeps the newest 20,000 comment ids.

Input configuration

fieldtypedefaultwhat it does
startUrlsarray[]Videos and Shorts (watch, youtu.be, /shorts/, /live/, /embed/ links or an 11-character id), community posts (/post/
searchQueriesarray[]Keywords: the top maxVideos videos of each YouTube search are read, from the country in searchCountry (YouTube shows each country a different search).
maxCommentsinteger100Comments read per video or post, in the order of sortCommentsBy; 0 = every comment YouTube lists. Replies are not counted in it (see maxRepliesPerComment). The summary row of each video says whether its list ended (COMPLETE), YouTube's list ended early (TRUNCATED) or this cap came first (CAPPED).
sortCommentsByNEWEST_FIRST / TOP_COMMENTS"NEWEST_FIRST"YouTube's own two orders. Newest first walks every comment; Top comments is YouTube's ranking, which ends at about 1,000–1,200 comments on a famous video.
oldestCommentDatestringOnly comments published on or after this date: YYYY-MM-DD, or a number of days ("1 day" = today's comments, "7 days" = the last week), weeks, months or years. It reads newest first. YouTube shows a comment's age in words ("3 weeks ago"), so the cut-off works to that precision.
includeRepliesbooleanfalseRead the replies of every comment delivered, each its own row with the parent comment's id (charged as a reply).
maxRepliesPerCommentinteger0Replies read per comment when Replies is on; 0 = every reply.
includeKeywordsarray[]Keep a comment only when its text contains one of these words or phrases as a whole word (any case: "art" does not keep "start"). A comment left out is free and does not count toward maxComments; a kept comment comes with all its replies.
excludeKeywordsarray[]Leave out a comment whose text contains one of these words or phrases as a whole word (any case). Free.
minLikesinteger0Keep a comment only with at least this many likes (YouTube rounds likes past 999: "1.6K" counts as 1,600). 0 = every comment.
maxVideosinteger10Videos taken from each channel (newest first), playlist (in its order) or search (top results).
channelTabvideos / shorts / both"videos"Which of a channel's lists maxVideos takes from.
maxPostsinteger0A channel's latest community posts whose comments are read too; 0 = none.
searchCountryUS / GB / CA / AU / NZ / IE / IN / PK / BD / PH / ID / MY / SG / TH / VN / JP / KR / TW / HK / DE / AT / CH / FR / BE / NL / ES / PT / IT / PL / CZ / RO / HU / GR / SE / NO / DK / FI / TR / UA / IL / AE / SA / EG / MA / NG / KE / ZA / BR / MX / AR / CO / CL / PE / LK / KZ / NP"US"Where each keyword search is read from (YouTube shows each country a different search). A video's comments are the same everywhere.
onlyNewbooleanfalseSkip every comment an earlier run with the same memory delivered (free); newest first, a video's walk stops at the first comment delivered before. For a scheduled task.
stateStoreNamestring""The name of the memory onlyNew keeps (letters, digits, '.', '_', '-'); empty = a name derived from the inputs.
maxItemsinteger0The most charged rows (comments and replies) of the whole run; 0 = no cap beyond maxComments and your charge limit.
proxyUrlstringYour own proxy (scheme://user:pass@host:port) for every call instead of ours. Leave empty: the Actor brings its own connections at no cost to you.

Errors

A run does not fail because of your input. When an input cannot be used — a date that is not a date, an id in the wrong form, two settings that contradict each other — the run ends Succeeded, charges nothing, and says what to change:

  • the status message starts with INPUT REJECTED;
  • the dataset holds one row, {"error": true, "code": "…", "message": "…"}, and no results;
  • the key-value store record ERROR holds the same object.

From code, check error on the first row before reading results.

A video with comments turned off, a missing video, a premiere, a members-only video or a live stream is not an error: it gets one free summary row with its status, and the run goes on with the others. A run that ends Failed is a fault on our side, never your input; it charges nothing, and we are alerted.

FAQ

Does it get every comment?

Every comment YouTube lists logged out, newest first, to the list's own end: 360 of the 362 our test video counts (the header counts held or removed comments it does not show). The video's summary row gives the exact count, what was delivered and the coverage. The Top order is YouTube's ranking and ends earlier on famous videos (1,199 of 2.46 million on our test); use Newest for all.

Is the comment date exact?

No exact time is shown logged out: YouTube writes "3 weeks ago" on every client and page. Each row has that text (published_text), an approximate time computed from it and the moment of the read (published_at_approx), and the range the text allows (published_after to published_before); published_at_is_estimated is true. A few sellers get an exact time from YouTube's Data API with their own key; this Actor needs no key.

Are likes exact?

Below 1,000, yes. Past it YouTube itself shows only a rounded text ("1.6K") to every viewer; the row gives 1,600 with likes_exact false and YouTube's text in likes_text. Comment dislikes are not shown by YouTube.

What are the free rows?

One summary row per video or post (type video or post): ok with the verdict and the counts, or why it has no comments — comments_off, not_found (two reads agree), upcoming, members_only, live (a live stream's chat is not comments), no_comments, failed, not_read. One row per channel, playlist or search (type list) and per value that names nothing (type input, invalid). Only comments and replies are charged. The summary rows share the dataset with the comments, so the default view and an export show them too: for comments alone keep the rows whose type is comment or reply (a filter in the spreadsheet, or one line in your code).

Does it need a YouTube account, cookies or an API key?

No. Everything is read from YouTube's own public data, logged out, with a plain HTTP client; nothing of yours is sent to YouTube. No YouTube Data API key and its quota.

I move from another YouTube comments Actor: which column is which?

The keys most YouTube comments Actors use map one to one: cid → comment_id, replyToCid → parent_id, type → type, publishedTimeText → published_text, comment → text, author → author_handle, authorIsChannelOwner → author_is_creator, replyCount → reply_count, voteCount → likes, hasCreatorHeart → is_hearted, videoId → video_id, pageUrl → video_url, commentsCount → video_comment_count, title → video_title. The input takes the same startUrls (with {"url": …} objects), maxComments, sortCommentsBy and oldestCommentDate.

Can it read live chat or translate comments?

Not in this version: a live stream's chat is not its comments (the video's row says live), and translation and sentiment are not built. The text comes as the commenter wrote it.

Integration

JavaScript

import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: 'YOUR_TOKEN' });
const run = await client.actor('hyperbach/youtube-comments-scraper').call({"startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "maxComments": 300});
const { items } = await client.dataset(run.defaultDatasetId).listItems();

Python

from apify_client import ApifyClient
client = ApifyClient('YOUR_TOKEN')
run = client.actor('hyperbach/youtube-comments-scraper').call(run_input={'startUrls': ['https://www.youtube.com/watch?v=dQw4w9WgXcQ'], 'maxComments': 300})
items = client.dataset(run['defaultDatasetId']).list_items().items

CLI

$apify call hyperbach/youtube-comments-scraper --input '{"startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "maxComments": 300}'

REST

curl -X POST "https://api.apify.com/v2/acts/hyperbach~youtube-comments-scraper/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' -d '{"startUrls": ["https://www.youtube.com/watch?v=dQw4w9WgXcQ"], "maxComments": 300}'

Support

apify@hyperbach.com

This page is generated from the Actor's schemas and a live sample — it cannot describe a field the Actor does not have.