Before you build a feature, your users have usually already told you what they want. They just told your competitors, in 1-star reviews.
I wanted a fast way to read those reviews for any app category and get a ranked list of pain points, questions and objections, each linked back to the original review. This post shows the free DIY way in Python, then the shortcut I ended up building.
Step 1: Find the top apps for a topic (free, no API key)
Apple's iTunes Search API is public. One request gives you the top apps for a keyword:
import requests
def search_apps(term, country="us", limit=5):
r = requests.get("https://itunes.apple.com/search", params={
"term": term, "country": country, "entity": "software", "limit": limit,
}, timeout=20)
r.raise_for_status()
return [
{"id": a["trackId"], "name": a["trackName"], "ratings": a.get("userRatingCount", 0)}
for a in r.json()["results"]
]
apps = search_apps("habit tracker")
for a in apps:
print(a["name"], a["ratings"])
Step 2: Pull recent reviews from the RSS feed
Each app has a public customer-reviews feed in JSON, about 50 reviews per page and up to 10 pages:
def get_reviews(app_id, country="us", pages=2):
out = []
for page in range(1, pages + 1):
url = (f"https://itunes.apple.com/{country}/rss/customerreviews/"
f"page={page}/id={app_id}/sortby=mostrecent/json")
entries = requests.get(url, timeout=20).json().get("feed", {}).get("entry", [])
if isinstance(entries, dict):
entries = [entries]
for e in entries:
if "im:rating" not in e:
continue # the first entry can be app metadata
out.append({
"rating": int(e["im:rating"]["label"]),
"text": e["content"]["label"],
"version": e.get("im:version", {}).get("label"),
})
return out
reviews = [r for a in apps for r in get_reviews(a["id"])]
critical = sorted([r for r in reviews if r["rating"] <= 3], key=lambda r: r["rating"])
print(len(reviews), "reviews,", len(critical), "critical")
Tip: sort critical reviews first. The 1–3 star reviews are where the product gaps are. 5-star reviews mostly say "love it".
Step 3: Turn reviews into insights with an LLM
Send the critical reviews to a model with a strict JSON format, and give every review an id so each insight can point back to its evidence:
corpus = "\n".join(f"r{i} [{r['rating']}★] {r['text'][:400]}" for i, r in enumerate(critical[:120]))
prompt = f"""From these app reviews, list the top pain points, questions,
objections and exact phrases users use. Return JSON only:
{{"pain_points": [{{"title": "...", "severity": 0-100, "evidence": "r12"}}], ...}}
Reviews:
{corpus}"""
Two things I learned the hard way:
- Make the model cite review ids, then drop any insight whose id doesn't exist. That kills most hallucinated "insights".
- Batch it. Around 60 reviews per call keeps answers sharp and cheap.
What it found for "habit tracker"
I ran this on the top habit tracker apps on the US App Store: 39 reviews from 4 apps, 30 insights, 47 seconds. A few of the real 1-star reviews behind the top pain points:
"The app updated and I no longer have lifetime access"
"I am subscribed to Pro and the widgets still say 'subscribe to pro'"
"Trying to profit off those who want to improve their lives is horrible"
"I created a few habits but they are not triggering at right time"
The pattern is clear without reading 39 reviews yourself: pricing changes and paywall bugs hurt trust more than missing features. If you're building in this category, "honest one-time price, no surprise paywall" is a positioning, not just a pricing choice.
The shortcut: run it as an Apify Actor
Maintaining scrapers, batching and JSON repair got old, so I packaged the whole pipeline as an Apify Actor: App Review Analyzer. It reads App Store reviews, Google Play reviews and (optionally) YouTube comments, and returns:
- pain points, questions, objections and exact user wording
- a severity score and frequency for each
- an evidence quote and a link to the original review
- an optional strategy report in HTML
Calling it from Python:
from apify_client import ApifyClient
client = ApifyClient("<YOUR_APIFY_TOKEN>")
run = client.actor("jaclfruit_296/audience-radar-actor").call(run_input={
"topic": "habit tracker",
"sources": ["app_store", "google_play"],
"country": "us",
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item["type"], item["score"], item["insight"], "|", item["evidenceQuote"])
It's pay per result (about $0.003 per review analyzed), so a run like the one above costs roughly $0.20. You can also point it at a dataset from any other scraper and it will analyze that text instead.
Wrap-up
- Competitor 1-star reviews are the cheapest user research you'll ever do.
- Always keep the link from insight to evidence, or you can't trust the output.
- Read the top 10 quotes yourself before you decide anything.
If you're validating a whole new app idea rather than a feature, I wrote a follow-up: How to check an app idea against real App Store data before you build it (link it here once it's live).
What do you use to research competitors before building? I'd love to hear other approaches in the comments.
Top comments (0)