People who watch a long review or a setup tutorial are informed critics, and they leave their complaints in the comments. This post covers choosing the videos, filtering by keyword and date, de-duplicating, tagging comments as bug, feature request, question or praise, and ranking the result for a product or support team.
Where the feedback is
In rough order of usefulness:
- Setup and tutorial videos. Viewers get stuck here: questions and bug reports.
- Independent reviews and unboxings of your product. Feature gaps, price, build quality.
- The same videos for a competitor. Their users' complaints are your opening; their praise is the bar.
- Your own channel.
Search YouTube for the product name plus "review", "unboxing", "tutorial", "problem" and "vs" your main rival. Keep public videos with comments on, published after the product version you care about. Private and members-only videos give you nothing, and videos made for kids never have comments.
Filter before you collect
Keyword. The Actor's keyword option passes your words to YouTube's own comment search, so you pay only for matching comments. It applies to top-level comments, so a complaint inside a reply thread is not searched. It is also YouTube's search, not a regular expression, so do not assume exact substring matching; check a sample. Run one theme at a time (battery, then crash, then refund). A filtered run shows what people say, not what share of viewers say it; for shares, also pull an unfiltered sample.
Date. Set "Only comments after" to the release date of the version you are studying, as a date (2026-03-01) or a relative period such as 3 months. With "Newest first" the Actor stops paging once it reaches older comments. Use "Top comments" instead for long-standing complaints other viewers have voted up.
Collecting the comments
Best Damn YouTube Comments Scraper is an Apify Actor that reads comments through the official YouTube Data API v3: no browser, no login. Give it video URLs (or a channel, and it reads that channel's most recent videos):
{
"startUrls": [
"https://www.youtube.com/watch?v=VIDEO_ID_1",
"https://www.youtube.com/watch?v=VIDEO_ID_2"
],
"searchTerms": "battery",
"sortBy": "time",
"publishedAfter": "3 months",
"maxComments": 200,
"authorData": "hash"
}
maxComments is per video. authorData: "hash" swaps the commenter's channel ID for a stable hash and drops name and avatar. Save the block as input.json and call the API:
curl -X POST "https://api.apify.com/v2/acts/josh99smith~youtube-comments-scraper/run-sync-get-dataset-items?token=$APIFY_TOKEN" \
-H "Content-Type: application/json" -d @input.json
Each record is one comment. Fields used below: success, commentId, commentUrl, text, likeCount, isByVideoOwner, authorChannelId. Unreadable videos come back as free records with success: false and an errorType such as comments-disabled.
De-duplicate, tag and group
De-duplicate because the same video can arrive twice (as a URL and through a channel), merged keyword runs overlap, and copy-pasted comments inflate a theme. Match on commentId and on normalised text. Skip comments where isByVideoOwner is true; the creator is not the audience.
Tag with rules where the first match wins (bugs, then requests, questions, praise) and group by theme. Counting distinct authorChannelId values works with hashed IDs, so you count people without knowing who they are.
import json, re
from collections import defaultdict
from apify_client import ApifyClient
client = ApifyClient("<YOUR_API_TOKEN>")
run = client.actor("josh99smith/youtube-comments-scraper").call(
run_input=json.load(open("input.json")))
seen, groups = set(), defaultdict(lambda: {"people": set(), "likes": 0, "links": []})
TAGS = [ # first match wins
("bug", r"crash|broke|stopped working|doesn'?t work|won'?t (start|connect)"),
("request", r"i wish|should (have|add)|please add|would be (nice|great)"),
("question", r"\?|how (do|can) i"),
("praise", r"love|great|works well"),
]
THEMES = {"battery": r"batter|charg", "app": r"\bapp\b|firmware", "price": r"price|expensive"}
for c in client.dataset(run["defaultDatasetId"]).iterate_items():
if not c["success"] or c["isByVideoOwner"]:
continue
t = c["text"].lower()
key = re.sub(r"\W+", " ", t).strip()
if c["commentId"] in seen or key in seen:
continue
seen.update([c["commentId"], key])
tag = next((n for n, p in TAGS if re.search(p, t)), "other")
for theme, p in THEMES.items():
if re.search(p, t):
g = groups[(tag, theme)]
g["people"].add(c["authorChannelId"])
g["likes"] += c["likeCount"]
g["links"].append(c["commentUrl"])
for (tag, theme), g in sorted(groups.items(), key=lambda kv: (-len(kv[1]["people"]), -kv[1]["likes"])):
print(tag, theme, len(g["people"]), g["likes"], g["links"][:2])
Replace THEMES with your product's vocabulary. When keyword themes stop being enough, embed each text with any sentence-embedding model, cluster the vectors (k-means is fine) and name each cluster by reading its top comments.
Turn it into a prioritised list
Rank each tag-and-theme group by distinct commenters, then total likeCount; a like is another viewer saying "me too". Each row needs the theme, tag, commenter count, likes, commentUrl links for context, and an owner: bugs to engineering or support, requests to product, repeated questions to whoever writes docs or tutorials, praise to marketing as things not to break.
Read a random sample from every tag before sharing. Keyword rules misread sarcasm: "love how it never connects" lands under praise here. Commenters are self-selected, not your customer base, so treat the list as a starting point.
Cost and limits
- Price. $0.0004 per delivered comment or reply ($0.40 per 1,000). Ten videos capped at 200 comments each is at most 2,000 comments, so at most $0.80. Videos with comments off, private or deleted videos, invalid URLs and quota errors are free; there is no start fee and a run stops at the maximum cost you set.
-
Quota. By default the Actor uses a built-in API key shared by all its users, with a daily quota of 10,000 units and one unit per page of up to 100 comments. If it runs out, the run stops cleanly and the remaining videos come back as free
rate-limitedrecords; the quota resets at midnight Pacific Time. Your own free key from the Google Cloud Console, pasted intoapiKey, gives you roughly a million comments a day. TheSUMMARYrecord in the key-value store lists comments per video, failures and quota units used. - Fewer comments than shown. YouTube's count includes replies and held or removed comments; the API returns what is currently public.
- Public data only. No login, so nothing private or members-only.
Treat comments as aggregate research
Comments are personal data under GDPR and similar laws, so you need a legitimate purpose. Work with themes, not people. Use hash or omit for authorData, do not contact commenters, and if you quote a comment in a report, leave the name out.
If this saves you time, a review on the Store page helps other people find it, and a star on GitHub helps too.
Disclosure: I built this Actor. Source: github.com/josh99smith/youtube-comments-scraper. YouTube is a trademark of Google; the Actor is not affiliated with Google.
Top comments (0)