DEV Community

Cover image for Find bug reports and feature requests in YouTube review-video comments
Joshua Smith
Joshua Smith

Posted on

Find bug reports and feature requests in YouTube review-video comments

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"
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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])
Enter fullscreen mode Exit fullscreen mode

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-limited records; the quota resets at midnight Pacific Time. Your own free key from the Google Cloud Console, pasted into apiKey, gives you roughly a million comments a day. The SUMMARY record 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)