YouTube Comments Scraper with Incremental Sync avatar

YouTube Comments Scraper with Incremental Sync

Pricing

from $0.11 / 1,000 comments

Go to Apify Store
YouTube Comments Scraper with Incremental Sync

YouTube Comments Scraper with Incremental Sync

Export public YouTube comments and replies with author details, likes and reply counts. Use incremental scans to collect new comments only across repeated runs.

Pricing

from $0.11 / 1,000 comments

Rating

0.0

(0)

Developer

Eli J

Eli J

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Export YouTube comments and replies

This YouTube comments scraper takes public video URLs or IDs and returns comment text, author details, likes and reply counts. Use the results for discussion analysis or comment monitoring. Incremental sync (the incremental setting) collects previously unseen comments, including replies added under old threads, without emitting remembered IDs again. No YouTube login or API key is required.

Try the prefilled input

The prefilled form contains three public videos, Top comments and replies, with a limit of 40 new rows per video and 120 for the run. Run it to inspect the dataset, then replace the sample videos with your own. The limits are ceilings, not guaranteed result counts.

{
"videoUrls": [
"9hetShMMp2s",
"B-ul3b0lz-o",
"D8PikZ1KhUo"
],
"maxComments": 40,
"maxItems": 120,
"includeReplies": true,
"sortCommentsBy": "TOP_COMMENTS"
}

Use watch, youtu.be, Shorts, embed or live video URLs, or bare video IDs. This collects video discussions; live chat and channel-video discovery are outside this product. Pass each URL or ID as a string. An API call with {} succeeds with guidance and collects nothing: the prefilled form does not become an omitted-input default.

Output and exports

Open Results and the Comments and replies dataset view to inspect rows or export them as JSON or CSV. The following excerpt contains literal fields from a retained 2026-10-10 run of the first sample video, 9hetShMMp2s. It omits other row fields for readability; comment content and counters can change between runs.

{
"recordType": "comment",
"id": "Ugzl-ky9OdrQg-UCbHp4AaABAg",
"videoId": "9hetShMMp2s",
"channelId": "UC2ojq-nuP8ceeHqiroeKhBA",
"title": "Claude Code Mods Are Game Changers. Set Up These 5 NOW.",
"text": "Thanks Nate - The handoff is probably the most useful one to prevent context rot",
"authorText": "@GuybrushWins",
"likeCount": 7,
"replyCount": 3,
"publishedTimeText": "7 days ago",
"publishedAtApprox": "2026-10-03T21:33:31.820663+00:00",
"publishedAtPrecision": "day",
"inputSource": "9hetShMMp2s",
"billingEvent": "comment-row"
}

Every dataset row has recordType (comment or reply), stable id, inputSource, scrapedAt and billingEvent. Each row also includes:

  • Text: text, comment and textDisplay.
  • Comment identity: cid, commentId and replyToCid.
  • Relative-time labels: publishedTime, publishedTimeText and commentPublishText.
  • Counts: likeCount, likesCount, voteCount, replyCount and commentsCount.
  • Author details: the author object, authorText, authorChannelId, authorAvatar and authorUrl.
  • Flags: verified, artist, creator, pinned, edited and creator-heart status.
  • Video details: videoId, channelId, pageUrl and title.

Like and reply counters are integers, with absent/null/blank counts represented as 0. Nonblank unparseable counts are null with their literal text preserved in likeCountText/replyCountText. Rounded source counts such as 1.2K project to 1200 and keep their displayed text. commentsCount is the available video discussion count, or null when unknown. authorText is the scalar author alias.

publishedAtApprox is derived at capture time from YouTube's English relative label; publishedAtPrecision is minute/hour/day/week/month/year. Months mean 30 days and years mean 365 days. It is an estimate, not an exact creation or edit timestamp. Unknown ages keep null estimates. When an oldestCommentDate filter requires an unreadable or missing age, that is a DATE_UNAVAILABLE error.

publishedAtDayApprox is the same estimate truncated to its UTC day and rendered as YYYY-MM-DDT00:00:00Z. Its precision is still publishedAtPrecision: a week/month/year label remains coarser than a day, even though the rendering contains a day. It is an approximate day from YouTube's relative text, not a creation or published date. Unknown ages have null estimates. publishedTime and publishedTimeText both preserve the literal relative label, including an edited suffix.

The following table maps exported fields to flat-author and nested-author formats while preserving measured values and JSON types. Date estimates use the same capture-time anchor; values observed at different capture times are not guaranteed equal.

Own value (JSON type)Flat-author formatNested-author format
authorText (string)authorauthor.name
authorChannelId (string), authorAvatar (string or null)Not mappedauthor.id, author.thumbnails; compose its author object from these and authorText
publishedTimeText (string or null)publishedTimeTextNot mapped
publishedAtDayApprox (string or null; approximate)Not mappedpublishedTime, its day rendering
replyCount, voteCount (integer; unknown null)replyCount, voteCountNot mapped
likeCountText, replyCountText (string)Not mappedlikeCount, replyCount
authorIsChannelOwner, hasCreatorHeart (boolean)same namesNot mapped
cid, comment, pageUrl, title, videoId, type (string); commentsCount (integer or null); replyToCid (string or null)same namestype
id, inputSource, text (string)Not mappedsame names

Pricing and billing units

Check the Actor's pricing panel for installed event prices. Event prices cover the Actor's platform usage, so computation, storage and proxy traffic are not billed separately.

For each requested video in each run, the first 20 new comments or replies use comment-intro; later rows use comment-row. Each emitted row has one billing event. Apify's standard Actor start event is charged once per run at up to 1 GB of memory. There is no query, reply-thread or cursor event. Remembered IDs produce no row event. Polls with no new rows are still charged the start event, so choose a useful polling interval and scan window.

Input settings

FieldTypeAPI defaultMeaning
videoUrlsarray of stringsemptyUp to 500 video URLs or IDs.
maxCommentsinteger20New comments plus replies per video; 0 performs no collection.
maxItemsinteger1000New output rows for the whole run; 0 performs no collection and returns guidance. At a positive cap, remaining inputs are not visited by the output loop; SUMMARY.stopReason is "maxItems" and coverage lists visited sources. Prefetched bootstrap requests may already have occurred.
includeRepliesbooleantrueFollow reply continuations; replies count toward maxComments.
sortCommentsBystringTOP_COMMENTSTOP_COMMENTS or NEWEST_FIRST, selected from delivered controls.
oldestCommentDatestringnoneInclusive approximate age boundary, using YouTube's relative label. An unreadable age is an error.
incrementalbooleanfalsePersist seen identities and emit only unseen rows.
stateStoreNamestringgeneratedNamed Apify key-value store for incremental STATE: 1–63 ASCII letters, digits or hyphens, starting with a letter or digit.
resetStatebooleanfalseAfter recovery, clear remembered IDs and replay old rows.
maxPagesPerSourceinteger100Page budget per discussion including reply pages; 0 performs no collection; maximum 10000.
maxRetriesPerRequestinteger2Fresh HTTP session retries after the first attempt; maximum 8. Missing-data retries stop after two total attempts.
maxRunSecondsinteger240Collection time budget; maximum 3600.
proxyConfigurationobjectdatacenterApify proxy configuration; defaults to Apify datacenter proxies. A blocked egress can require a different proxy route.

Omitted sources never trigger collection. Superseded channel/video-listing/discovery controls are rejected with INVALID_INPUT. Metadata and relative-age parsing use English/US public responses.

Collect new comments only

  1. Set incremental to true.
  2. Keep the same source set, or supply a stateStoreName such as my-video-comment-sync and reuse that name.
  3. Run polls serially. Each poll scans the requested window and emits IDs that its store has not remembered.

"New" means previously unseen by this store. A later poll can find older comments beyond the first poll's cap. Caps count newly emitted rows; page and time budgets bound rescanning. Increase the scan window when you need to reach older threads or their new replies. Set resetState to true to make remembered rows eligible again.

State and recovery

Generated names hash sorted unique canonical submitted video IDs plus the comment/reply record families. Equivalent video URL/ID spellings share a store. Caps, filters, sorting, reply inclusion, defaults and transport budgets do not change the key; changing the source set does. An explicit name shares history across different source sets or scan windows.

STATE version 2 snapshots remembered comment/reply IDs and output/charge receipts grouped by owner channel, with per-video exhausted-scan markers and an applied journal sequence.

Up to 40 new rows are buffered; a JOURNAL reservation precedes each batch, including earlier committed receipts since the last source snapshot. Rows are pushed in homogeneous billing-event groups. STATE checkpoints follow every 400 committed receipts and each source with changes; the applied sequence makes an old reservation harmless. Version 1 single-row journals are reconciled and migrated before collection.

Recovery reads the pending batch from its prior run dataset tail, checks its receipt and remembers exactly the written prefix. For priced events, charged-event totals must also match. Unpriced receipts are valid when no Actor event price is installed. A foreign writer must be terminal; the current run ID can reconcile during its own restart.

A restarted run also reads its existing default dataset once, in bounded pages, to rebuild total and per-video row counts so caps and the first-20 intro tariff remain per run. For a non-incremental restart, those same pages also restore the current run's IDs in memory so already delivered comments are not duplicated. Fresh runs have empty default datasets and skip this read.

Normal emission performs no per-row key-value write or dataset read. Incremental persistence uses up to two writes per batch: one JOURNAL reservation and a STATE snapshot at source/end. A video that yields one 40-row batch uses two writes. In the retained 1,000-row single-video test, batches shared snapshots until the 400-receipt checkpoint and used 28 writes. Legacy migration can add one journal-retirement write. Small positive polls have higher fixed storage overhead.

SUMMARY.coverage marks exhausted videos complete; capped and failed videos are incomplete, and failures are listed in ERRORS. Partial-scan IDs remain remembered, and lastSuccess advances only on exhaustion.

Before interrupted output is remembered, the dataset prefix and its receipt must agree. A foreign writer must be terminal; a matching current run ID can reconcile its own interrupted output. An ambiguous receipt stops with STATE_UNCERTAIN and retains the journal for review; it is never replayed blindly. resetState clears history only after recovery on a fresh run and makes old rows billable again. A resumed run with existing output retains the reset it already applied and never clears those delivered IDs again. Deleting the named store also removes history.

Coverage, errors and limits

Only logged-out public discussions are supported. Errors and unresolvable inputs go to ERRORS, never the dataset. A run succeeds when at least one row was delivered and no charge receipt is uncertain, even if a later source or page fails. See ERRORS and SUMMARY.coverage for the incomplete work. It fails when input is invalid, when incremental state cannot be recovered, when a charge receipt is uncertain, or when no row was delivered and every attempted source failed without collection stopping at a cap. Valid empty or filtered videos can succeed with zero rows. SUMMARY records source outcomes, caps, charge-limit stop and transport metrics. No exhaustive-history or exact-time claim is made outside the requested windows.

Collection and fallback

Collection uses cookie-free HTTP/JSON, with isolated Chromium fallback after retryable failures. Video identity, title, owner and the discussion token come from one InnerTube next request by video ID; collection fetches neither player nor the watch page. Unavailable videos and comments-off responses are source errors and are not retried. Malformed discussion data is checked inside the retry/fallback boundary. A failed fallback is reported as an error, not an empty video.

Each HTTP attempt has a total wall deadline of 25 seconds or the remaining run budget, whichever is shorter. This includes up to two seconds reserved for client close. Attempt timeouts use the NETWORK retry and fallback path. Malformed data or rejected request context gets at most two HTTP attempts, with 0.1-second retry spacing. Other retryable failures use 0.4 seconds. Successful first attempts have no fixed wait. Up to four independent reads overlap; row, billing and journal commits stay serial. While the next comment page for a video is requested, one received page can wait in a queue, so a run can request comment pages it does not use. One measured run of three videos at the default 20 comments per video made 8 requests instead of 6 and received about 13 KB (5%) more response bytes.

Browser requests share a 45-second budget for queueing, work and cleanup, with up to five seconds reserved for cleanup. Shorter remaining run budgets limit both work and cleanup. Browser work runs one request at a time. Contexts are fresh and nonpersistent, clear cookies before routed requests and block service workers. The Actor never logs in.

Use at least 512 MB memory. SUMMARY.transport reports encoded bytes of completed HTTP responses, browser fallback counts and browser body bytes. Attempts ended by the attempt deadline are not byte-counted. These byte counts are not a complete proxy invoice.

Cold starts

Cold image pulls can dominate launch time. In five scored ten-comment runs on 2026-10-10, two pulls took 14–18 seconds. This infrastructure tail is separate from request work. Requests can also be slow: a later five-run ten-comment cohort had a 19.673-second run containing an 11.552-second continuation request. In that run the request stayed inside the 25-second attempt limit; its image pull took 0.002 seconds. The request interval includes proxy and network time; its cause was not isolated.

Reading failures

The Request errors output opens the ERRORS record when present. A clean first attempt can have no ERRORS record. A same-run restart can retain an earlier ERRORS record while SUMMARY describes the current attempt.

Common questions

Why did a successful run return no rows?

Empty input and zero row or page caps return no rows successfully. On these no-work paths, SUMMARY contains pushed, allFailed and guidance; it has no stop reason or coverage. For a collected scan, including an empty video, filtered window or incremental window with no unseen IDs, inspect SUMMARY.stopReason and SUMMARY.coverage.

Why are some videos missing from coverage?

Once a positive maxItems limit is reached, remaining inputs are not visited by the output loop. Prefetched bootstrap requests may already have occurred. SUMMARY.stopReason is maxItems, and coverage lists only visited sources. A source error can also leave a visited video incomplete; inspect ERRORS and its coverage entry.

Are the published dates exact?

YouTube supplies relative labels such as “7 days ago.” The Actor preserves that text and labels its inferred timestamps approximate. Coarse labels stay coarse even when a day-shaped alias is rendered.

Can two runs share the same incremental store?

Use serial runs. Concurrent writers to the same store are unsupported. Interrupted output is reconciled before collection or reset; an uncertain receipt stops for review instead of replaying blindly.

Help

When reporting a problem, include the run ID, your input, the relevant ERRORS code and the incomplete coverage entry. Remove proxy credentials and other secrets from anything you share. Keep the named incremental store until an interrupted run's receipts have been reviewed.