# Changelog of YouTube Comments Scraper — Replies, Likes & Channel Videos (`steadyfetch/youtube-comments-scraper`) Actor

- **URL**: https://apify.com/steadyfetch/youtube-comments-scraper/changelog.md
- **Full Actor documentation**: https://apify.com/steadyfetch/youtube-comments-scraper.md

## Changelog

### 1.0.21 — 2026-10-17

- **A channel link on a short run now lists its newest uploads instead of stopping before it starts.** On a run whose time limit was under about two minutes, a channel link was turned away at once with a "skipped for time" row and no comments, even when there was time to list the channel and collect comments from its newest video. Such a run now lists the channel whenever there is still time to read one listing page and start on the first video. A run that really is too short still gets the same honest row, and nothing is charged for it. Runs of two minutes or more behave exactly as before.

### 1.0.20 — 2026-10-13

- **A run with "Max run seconds" left at its default now uses the run timeout you set.** Before, a run that set a long run timeout but left "Max run seconds" untouched still stopped after 600 seconds and left the rest of the comments it was asked for undelivered. Now the run keeps collecting until the run timeout you set, up to 3,600 seconds. Any other "Max run seconds" value you set still applies exactly as before, and a run stopped by the run timeout now says so instead of pointing at "Max run seconds".
- Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.19 — 2026-10-06

- Links to sibling actors now show those actors' current names.

### 1.0.18 — 2026-09-29

- **When YouTube is limiting requests for a while, a video is no longer reported as removed.** YouTube sometimes answers "This content isn't available, try again later" when it is rate-limiting requests, and the run used to report that video as removed or unavailable. It now gets the re-run row instead, so you know to try it again in a little while, and nothing is charged for it.
- Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.17 — 2026-09-29

- **When YouTube stops serving a video's comments and your account already has them, the run now says exactly that.** Before, the whole rest of a large ask was reported as "depth reached" — "5,000 depth reached" on a video with a few hundred comments, all of them already yours. Now the part of your ask your account already holds is reported as comments you already have, skipped and not charged, and only what you asked for past the end of the feed is reported as past where YouTube stopped serving comment pages; on a one-video run the run page says where that was.
- **A re-run of a video you already hold in full is confirmed on its first page.** When an earlier run read a video's comment order to its end (or found its top already in your account), a later run at the same comment count reads one page to confirm nothing is new and stops, instead of reading the whole feed again. A new comment changes the count, and the run then reads on as before.
- **A run that Apify moves to another server mid-run now finishes the video it was on.** The moved run used to mistake the comments it had already delivered for the end of the feed, stop, and report comments as not delivered that were already in your dataset. It now carries on to the comments still owed, reports the run's own totals, and never sends or charges a comment twice.
- **A playlist embed link and a channel's live-stream embed link are no longer read as video ids.** A playlist embed now gets the playlist note, and a live-stream embed goes through the channel door to that channel's newest uploads.
- Nothing changes about the price, which rows are charged, the input fields or the output columns.

### 1.0.16 — 2026-09-26

- **A run cut off before it finishes is now accounted for.** The run now notes its own start in its internal record, so a run that ends without finishing — a hard abort, a crash or a timeout — is still accounted for on our side. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

Every entry here is one build on the Builds tab, 1:1, newest first.

### 1.0.15 — 2026-09-24

- **The `videoUrls` field is no longer marked required in the input form.** An AI agent or API call that leaves it out gets the same sample run a bare Start always gave, and one that sets it gets the comments of those videos, exactly as before. No price, input field, output column or charged event changed.

### 1.0.14 — 2026-09-24

- **A run that Apify restarts after it has already delivered keeps its own record of that delivery.** If the restarted part of a run delivers nothing new, the run's internal record now keeps what the earlier part delivered and charged, with a note of the restart, instead of being rewritten as if nothing had been delivered; one line in the run log says so. Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

### 1.0.13 — 2026-09-22

- **A failed internal bookkeeping write no longer prints its storage address in the run log.** Nothing changes about what is delivered, which rows are charged, the price, the input fields or the output columns.

### 1.0.12 — 2026-09-19

- **The whole "Maximum cost per run" you set is now spent on comments; before this build the run held back about a twelfth of it.** Your cap pays for comments and nothing else — the compute, the proxy and the data behind the run are ours, not yours — but the run was still reserving roughly 8% of your cap against those costs before it planned a single comment. So a cap worth ten comments bought nine, and the tenth was money you had authorised and could not spend. That reserve is gone. A run at its cap still ends the same honest way it always did: it stops itself, tells you what it cut and why, and charges you for nothing it did not deliver. No price, input field, output column or charged event changed.

### 1.0.11 — 2026-09-18

- **The sentences saying what you were NOT charged for now survive the run page's 500-character limit — and a proxy that hangs can no longer outlive your own time limit.** The three uncharged counts (comments you already had, comments reached twice and delivered once, comments served from your account's memory) all sat in the disposable half of the status line and went together on 136 of the 192 endings a run can have, once at 342 characters with 158 still spare, while the 90-character "open an issue" line stayed. They now fold into one sentence naming the total and pointing at the receipt row, which breaks each kind down, and the support ask is what gives way first. Separately, the run's remaining time now bounds the proxy setup call: it was capped at 20 seconds in theory and at nothing in practice, so a slow setup could run past "Max run seconds" with nothing able to end it.

### 1.0.10 — 2026-09-16

- **What a comment costs is now on the first screen of this listing, not a page and a half down.** The per-1,000 price was only in the table below the fold, so anyone skimming the top — or any agent reading only the opening — could not price a run without scrolling. The opening now carries the per-1,000 figure on Gold and above and on the Apify free plan, and the list of what never carries a result fee. The "Include replies" switch and the one-list "Start URLs" door also gained worked examples and moved their never-charged sentences up, where an AI agent’s tool screen can still read them. Nothing about what is collected or charged changes.

### 1.0.9 — 2026-09-15

- **"Max run seconds" now ends the reading, not just the starting.** The run already refused to ask
  YouTube for a new page once your limit was close, but a page it had already started fetching was
  held only by its own 30-second network timer — nothing tied that to your limit — so the last read
  of a run could still be open after the limit had passed, and it ate most of the time the run keeps
  back to push the rows it already has, write the receipt and close cleanly. Every read is now sized,
  at the moment it starts, to what the run actually has left; a read your limit cuts short is
  reported as your own time limit on its own uncharged row, in the same words a video skipped for
  time already used, and the rows already collected and the receipt still land. A run with time to
  spare is unchanged — every read still gets its full timeout. Nothing else moved: no input field,
  output column, charged event or price changed.

### 1.0.8 — 2026-09-14

- **A run that cannot open the memory of what you already have now says so, instead of quietly
  charging for it again.** The promise on this page is that a comment your account already has is
  never bought twice, and the memory that keeps it is a key-value store in your own Apify account.
  A run started with a scoped API token in restricted-access mode cannot open that store at all —
  so every comment you already owned was collected and charged a second time, and nothing on the
  run page, in the rows or on the receipt told you why. It now does: an uncharged note row at the
  top of the dataset, a clause on the run page and a line on the receipt all name the cause and the
  fix — give the token key-value store Read, Write and Create permission, or set Actor runs to Full
  access, under Settings → API & Integrations, or start the run from the console. Every other reason
  the memory can be unavailable keeps the words it had. Nothing else moved: no input field, output
  column, charged event or price changed.
- **And a token that can READ that memory but not WRITE it now says so too.** That one is the one
  that breaks "never pay for the same comment twice" while looking like a perfect run: every comment
  you already had was still recognised and skipped, but nothing this run delivered could be written
  down — so your NEXT run bought and charged all of it again, with nothing anywhere to say why. The
  run now names it the moment the first write is refused, on the run page, on its own uncharged row,
  on the receipt and in the run log, and tells you the permission to add: key-value store Write (and
  Create) under Settings → API & Integrations, or Actor runs set to Full access. A run with a full
  token is unaffected, and nothing about what is charged changed.

### 1.0.7 — 2026-09-13

- **The pointer to the channel-transcript actor now names it by its current title.** If what you
  want is every video on a channel as text rather than its comments, this page points you at that
  actor — and it is now called YouTube Channel Videos Transcript Scraper — Bulk Video to Text,
  renamed so the name says what a plain run there delivers (the newest 50 videos per channel, up
  to 5,000). Same actor, same link; only the words changed. Nothing here moved: no input field,
  output column, charged event or price changed.

### 1.0.6 — 2026-09-13

- **The pointer to the transcript actor now names it by its current title.** If what you want is the
  spoken words inside a video rather than its comments, this page points you at that actor — and it is
  now called YouTube Transcript API & Scraper — YouTube Subtitles, Speech AI. The link goes to the same
  actor as before; only the words a reader sees changed.
- Nothing else moved: no input field, output column, charged event or price changed.

### 1.0.5 — 2026-09-12

- **A link in your rows can never carry a network address, whatever form it arrives in.** Every link
  this actor delivers — the video link, the author link, and the link you pasted handed back to you —
  already went through our own URL hygiene before reaching your dataset, and that pass had a gap:
  where a link is written with HTML-escaped separators (`&amp;` in place of `&`), the address
  parameter was not recognised by name and could survive into a row. It is now removed however the
  link is written — escaped, plain or legacy separators, upper or lower case, and inside a link that
  wraps another link. Nothing else in a link moves: the expiry, the signature and every routing
  parameter are delivered exactly as they were served, so the links keep working exactly as before.
  No column, no input, no price and no charge changes.
- **Maintenance only: nothing about your runs, your rows, your events or your prices changes.** This
  actor shares its connection layer with our YouTube transcript actors, and that layer was fixed
  there: a minimum gap is now held before every kind of request, and a connection that answered can
  be carried over to the next video instead of a new one being opened each time. Neither half moves
  anything here — this actor makes only the one kind of request, which already waited that gap, and
  its own retry ladder already carried a connection that answered from one video to the next. The
  shared file is updated here so the two stay identical, which is how a fix on one of them reaches
  the other.

### 1.0.4 — 2026-09-12

- **A run that stopped because your account already had everything now says so in the run's own
  record, not just in the row.** Last build the uncharged row learned to say that the ten-pages-of-
  repeats bound is this actor's own and not YouTube's — but the run's summary still filed it under
  the platform running out of comments, so the row and the summary said opposite things about the
  same video. The summary now names it separately, as a stop rather than a miss: nothing failed,
  nothing ran out, and nothing was charged. "Depth reached" from now on means only what it says —
  YouTube's own comment order running out, or this actor's own page limit on one video.

Your inputs, your events, your prices and every column on your rows are unchanged, and nothing you
have already collected needs re-running.

### 1.0.3 — 2026-09-12

- **A re-run whose first pages hold only comments you already have now says so, instead of blaming
  YouTube's ceiling.** This actor stops walking a video after ten pages running that bring back
  nothing new, so it does not keep buying pages you would not be charged for anyway — but the
  uncharged row it left said YouTube had stopped serving that video, which was not true and told you
  a re-run in a different order would not help. The row now names the real reason, how many comments
  were skipped because your account already had them, and the three things that do help: **Include
  comments you already have**, the other comment order, or a later run once new comments have
  arrived. The run page's own line says the same rather than "depth reached".
- **Our own 400-page bound on a single video is named as ours**, so a video that hits it is never
  reported as YouTube running out of comments.
- **A video cut off by the run clock mid-walk now carries its two numbers on every path.** One of
  those paths still read as though nothing had been collected — on a video that had already delivered
  959 charged comments. It now says how many it collected, how many were left, and which setting to
  raise. The same is true when a feed stops answering part-way through a video: the row no longer
  says there was no result fee over rows that were charged.

Your inputs, your events and your prices are unchanged, and nothing you have already collected needs
re-running.

### 1.0.2 — 2026-09-14

- **A video the run stopped part-way through now says how far it got.** The uncharged row for a
  video whose walk was ended by one of your own limits used to read as though nothing had been
  collected from it — on a video that had already delivered 1,140 comments. It now says how many
  comments it collected from that video, how many were left behind, and which setting to raise to
  go back for them.
- **Every delivered row now carries `chargedEvents`, naming the event it was billed as**, so one
  row can be checked against your invoice on its own instead of by counting the whole dataset. The
  column was already described in the output schema; from this build it is in the row.

Your inputs, your events and your prices are unchanged, and nothing you have already collected needs
re-running.

### 1.0.1 — 2026-09-14

First public build.

- A YouTube video link returns its comments as rows: the comment's own stable id, the text, the
  author with their channel id and YouTube's verified, uploader and artist badges, the like count,
  the reply count, the pinned and hearted-by-the-uploader flags, and the video and channel the row
  came from. Watch links, `youtu.be` links, Shorts, live, embed and `/v/` URLs and a bare
  11-character id are all read.
- **A channel link is a first-class input.** A handle, `/@handle`, `/channel/UC…`, `/c/…` or
  `/user/…` returns the comments under that channel's newest uploads, `videosPerChannel` of them.
  No other comments scraper on this store takes a channel link.
- **Replies are rows at the same price, folded under the comment they answer.** With
  `includeReplies` on (the default), a comment with a thread brings that thread, each reply carrying
  `isReply` and `parentCommentId`, counted inside your own per-video limit. There is no second
  event and no surcharge for a thread.
- **Your limit is exact and it is per video.** `resultsLimit` (default 100) is what each link
  returns; `maxItems` (default 1,000) caps the whole run. `maxComments`, `commentsPerVideo`, `limit`
  and `count` — the names other comment scrapers use — are read as `resultsLimit`, with one
  uncharged note row saying so, rather than being ignored.
- **Two orders, and they are YouTube's own two:** `sort: top` (the default) and `sort: newest`. An
  order we do not know is refused on an uncharged row naming the two that work, never swapped for
  something else.
- **How deep a sort goes is documented, because it is YouTube's ceiling and not ours — and it is
  approximate.** Walked to exhaustion twice, a day apart: `top` stops at about 1,200 comments per
  video whatever the video's size (around 1,220 on a 2.46-million-comment video, around 1,130-1,140
  on a 5,171-comment one — 1,130 on the first walk and 1,137 on the second, both a clean end), while
  `newest` was still serving at 3,000 with more to come. YouTube decides when to stop serving, so
  the figure moves a little between runs and the README states a range rather than a number. When
  the pages run out before your limit, the run leaves an uncharged `depth_reached` row saying how
  far that order reached and naming `newest` as the deeper door.
- **Dates are shipped at the precision YouTube actually publishes.** The surface carries a relative
  label and no timestamp, so the row carries `publishedText` verbatim, a derived `publishedAt` and
  `publishedAtPrecision` (`minute` to `year`) saying how precise that derivation is. A comment
  labelled "1 year ago" is never returned as a millisecond stamp.
- **Rounded like counts are named, not hidden.** Above 999 YouTube publishes "314K" rather than a
  number, so the row carries `likeCount`, `likeCountText` and `likeCountIsApproximate`.
- **A video with nothing to give answers with a typed, uncharged row**, and the answers are told
  apart: `comments_disabled` (the uploader turned comments off — read from the video's own page
  before any comment page is requested), `no_comments`, `removed_or_unavailable`,
  `private_or_members_only`, `age_restricted`, `region_blocked`, and the channel door's
  `channel_not_found`, `not_a_channel` and `channel_no_videos`. None of them is charged.
- Account memory so a schedule pays only for what is new: every comment delivered to your account is
  remembered in the key-value store `yt-comments-account` and skipped on the next run, with
  `includeSeen` to get them back anyway, uncharged, and `resumeFromDatasetId` to carry the memory
  across accounts.
- A run clock (`maxRunSeconds`, default 600) and `maxTotalChargeUsd` as a hard ceiling. A cap or the
  clock ends the collecting, never the delivering: comments already in hand are always written out,
  and the last row of every run is a receipt that reconciles the invoice from the dataset itself.
- **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 the columns can
  be read before a big run is planned. 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.
- One charged event, `comment`, billed only when a comment lands in your dataset. No start fee and
  no result fee.
