The Mine Works
← All posts
tutorial October 8, 2026 · 7 min read Updated October 8, 2026

Monitor the Newest Comments on Your YouTube Videos Every Day

Read the newest comments on your YouTube videos every day with YouTube Comments Scraper, keep only unseen ones, and flag questions you have not answered.

Try the scraper

The actor referenced in this article. Pay only for results delivered.

View the scraper →

The first days after a video goes up are when an unanswered question costs the most: the viewer who asked is still around, and others are reading the thread. Checking by hand works on a quiet week. It breaks down with several videos, several people answering, or videos on channels you do not own, such as a partner’s launch, and it leaves no log of what was already seen.

Try it live: YouTube Comments Scraper: Comments and Replies, No Login. Pay per result delivered. Failed and empty results are never charged.

This guide sets up a daily run of the YouTube Comments Scraper on your latest videos, keeps only comments you have not seen, and flags questions your channel has not replied to yet.

What you get and who it is for

Each row is one comment or reply. For monitoring, these fields do the work:

FieldUsed for
comment_idThe dedupe key; it does not change between runs
textFinding questions and complaints
published_time_text, published_atHow old the comment is
reply_countReplies YouTube says the comment has
is_reply, parent_comment_idLinking replies to the comment they answer
author_is_channel_ownerYour channel wrote this row
is_heartedYour channel hearted the comment
comment_urlOpens the video with the comment highlighted, ready to answer

It suits creators and their community managers, brands that post product and launch videos, and agencies watching several client channels from one script.

The input

{
  "videos": [
    "https://www.youtube.com/watch?v=VIDEO_ID_1",
    "https://www.youtube.com/shorts/VIDEO_ID_2",
    "VIDEO_ID_3"
  ],
  "maxComments": 100,
  "sortBy": "newest",
  "includeReplies": true,
  "maxRepliesPerComment": 10
}
  • Replace the placeholders with your own videos. Watch links, Shorts links and bare 11 character ids all work, mixed in one list, and five videos are read at a time.
  • sortBy set to newest puts the most recent comments first, which is the order a monitor needs.
  • maxComments is per video and counts replies too. Set it a little above what a video collects in a day.
  • includeReplies with maxRepliesPerComment at 10 reads up to 10 replies under each comment, which YouTube serves in one request, so the script can check whether your channel already answered.

What the output looks like

Two rows, trimmed, from our run on Rick Astley’s Never Gonna Give You Up video on October 7, 2026. That run used Top order, so sort_by reads top; a Newest run returns the same fields with sort_by set to newest. Commenter handles are masked and their channel fields dropped.

[
  {
    "video_id": "dQw4w9WgXcQ",
    "video_channel_name": "Rick Astley",
    "comment_id": "UgyXbabT1C58g-F4AGt4AaABAg",
    "comment_url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ&lc=UgyXbabT1C58g-F4AGt4AaABAg",
    "text": "Forgot I was here. I rickrolled myself",
    "author_name": "@S***************",
    "author_is_channel_owner": false,
    "likes": 2,
    "reply_count": 0,
    "is_hearted": false,
    "is_reply": false,
    "published_time_text": "9 hours ago",
    "published_at": "2026-10-07T02:11:30.100Z",
    "position": 49,
    "sort_by": "top",
    "scraped_at": "2026-10-07T11:11:30.100Z"
  },
  {
    "video_id": "dQw4w9WgXcQ",
    "comment_id": "Ugy0K3HrqLSgIwxXizl4AaABAg",
    "text": "Fun fact is that the fans were so surprised that Rick was twenty one? And he had a voice of like a twenty nine year old",
    "author_name": "@M***************",
    "author_is_channel_owner": false,
    "likes": 2,
    "reply_count": 0,
    "is_hearted": false,
    "is_reply": false,
    "published_time_text": "6 days ago",
    "published_at": "2026-10-01T11:11:33.623Z",
    "position": 91,
    "sort_by": "top"
  }
]

Even in Top order, 9 of the 100 rows in that run said “1 day ago” or less, and 19 had no replies at all. The first row shows how published_at is built: the scrape time minus 9 hours, so it is good to the hour. The second row is a warning about simple rules: it contains a question mark, yet it is a remark, not a question. On the same day we ran Me at the zoo, the first video ever uploaded to YouTube, and the uploader’s own comment came back with author_is_channel_owner set to true, the flag the script below relies on.

Run it in Apify Console

  1. Open https://apify.com/themineworks/youtube-comments-scraper and click Try for free.
  2. In YouTube videos or Shorts, paste your recent videos, one per line.
  3. Set Max comments per video to 100 and Sort comments by to Newest first.
  4. Switch on Include replies and set Max replies per comment to 10.
  5. Click Start and read the rows in the Output tab.
  6. Click Export to download CSV, JSON or Excel.

A Console export is fine for a one-off check. The daily job needs memory of what you have seen, which is what the script adds.

Run it from Python

pip install apify-client, then:

import csv
import json
from pathlib import Path
from apify_client import ApifyClient

SEEN_FILE = Path("seen_comment_ids.json")
seen = set(json.loads(SEEN_FILE.read_text())) if SEEN_FILE.exists() else set()

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("themineworks/youtube-comments-scraper").call(run_input={
    "videos": [
        "https://www.youtube.com/watch?v=VIDEO_ID_1",
        "https://www.youtube.com/shorts/VIDEO_ID_2",
        "VIDEO_ID_3",
    ],
    "maxComments": 100,
    "sortBy": "newest",
    "includeReplies": True,
    "maxRepliesPerComment": 10,
})

rows = []
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    if item.get("_type") == "status":
        print("no comments:", item.get("input"), item.get("note"))  # free row
        continue
    rows.append(item)

# threads your channel already replied to, and how many replies were read
answered, replies_read = set(), {}
for r in rows:
    if r["is_reply"]:
        parent = r["parent_comment_id"]
        replies_read[parent] = replies_read.get(parent, 0) + 1
        if r["author_is_channel_owner"]:
            answered.add(parent)

new_rows = [r for r in rows if r["comment_id"] not in seen]

waiting = []
for r in rows:
    if r["is_reply"] or r["author_is_channel_owner"] or "?" not in r["text"]:
        continue
    if r["comment_id"] in answered or r["is_hearted"]:
        continue
    all_read = r["reply_count"] <= replies_read.get(r["comment_id"], 0)
    r["flag"] = "no reply from you" if all_read else "check: more replies than were read"
    waiting.append(r)

fields = ["video_id", "comment_id", "is_reply", "text", "likes", "reply_count",
          "published_time_text", "published_at", "comment_url", "flag"]

with open("new_comments.csv", "a", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    if f.tell() == 0:
        writer.writeheader()
    writer.writerows(new_rows)

with open("waiting_for_reply.csv", "w", newline="", encoding="utf-8") as f:
    writer = csv.DictWriter(f, fieldnames=fields, extrasaction="ignore")
    writer.writeheader()
    writer.writerows(waiting)

seen.update(r["comment_id"] for r in rows)
SEEN_FILE.write_text(json.dumps(sorted(seen)))
print(f"{len(new_rows)} new comments, {len(waiting)} questions waiting for a reply")

new_comments.csv grows by one day’s new comments per run. waiting_for_reply.csv is rebuilt each run from everything read that day: top comments with a question mark, not written by your channel, not hearted, and with no reply from your channel among the replies read. When a comment has more replies than were read, it is marked for a manual check rather than called unanswered, since your reply could sit further down the thread. Author handles stay out of both files.

Run it every morning

The dedupe file lives wherever the script runs, so the simplest setup is to run the script once a day from cron or any job runner, with a line such as 0 7 * * *. If you would rather keep the schedule in Apify, save the input as a task, open Schedules, create a schedule with the same cron expression and pick the task. Then add a webhook on a successful run that calls a small service of yours, which fetches the dataset and applies the same comment_id filter. The actor has no option to return only new comments, so that filter always runs on your side. From there, send the waiting list to Slack, a Google Sheet or email. If once a day is too slow during a launch, every six hours works for most channels. The same every-morning pattern works on review sites, where new Trustpilot reviews are picked up as they appear.

What it costs

$0.20 per 1,000 comment rows on the Bronze plan, and the same $0.20 on Silver, Gold and the higher plans, plus $0.005 per run. Five videos at 100 rows each, every day for 30 days: 15,000 rows, $3.00, plus 30 start fees of $0.005, $0.15. Rows you saw yesterday are charged again because they come back in the next run, so lowering maxComments to 50 on all five videos halves the row cost.

Limits worth knowing

  • No memory between runs. Dedupe on comment_id yourself, and expect to pay for the overlap.
  • If a video gets more comments between runs than maxComments, the oldest of them fall outside the window and are never read. Run more often or raise the number.
  • A new reply under an older comment only shows up if that comment is inside the window you read.
  • published_at is as exact as YouTube’s relative text: minutes or hours for new comments, years for old ones.
  • The question mark rule is rough. It misses questions written without one and catches remarks like the second row above. Treat the waiting list as a queue to read, not a verdict.
Related Actor

Explore the scraper referenced in this article: inputs, outputs, and pricing, then run it on Apify.

Frequently asked questions

Does the actor remember which comments it already returned? +

No. Every run reads the newest comments again and charges for every row it returns. Store the comment_id values you have seen and drop them on your side, as the script in this guide does.

How exact is published_at on a new comment? +

It is the run time minus the relative age YouTube shows, so a comment marked 9 hours ago is placed to the hour and one marked 6 days ago to the day. For a daily monitor that is close enough to sort and filter on.

How many comments should I ask for each day? +

Set maxComments a little above the number of comments and replies a video gets between two runs. Too low and you miss comments; too high and you pay $0.0002 for each row you have already seen.

Does it work on Shorts? +

Yes. Shorts links and their ids go in the same videos list as regular videos, and the rows link back to the /shorts/ address.

How does the script tell that I already answered a comment? +

A reply written by your channel has author_is_channel_owner set to true, and a comment you hearted has is_hearted set to true. Either one takes the comment off the waiting list.

Apify Store

Find a ready-made scraper for your job

Apify has over 80,000 scrapers and automations, ours included. Start free with $5 of platform credit every month.