Find apps whose ratings crater right after a release — with users naming the exact regression — and pitch the fix. Timed to the release, not the reputation.
app-regression-radar builds a prospect list of app publishers who just shipped a bad release. Because every app-store review is tagged with the app version it was written against, a fuzzy "reputation" signal becomes a sharp, event-anchored one: "1-star share on v4.0 is 87% vs 0% on the prior version, all mentioning crash-on-launch, and the developer hasn't replied." It pulls reviews from Google Play and the Apple App Store via official Apify actors, buckets them by version, detects the regression, and emits a prospect record with the offending version, the defect cluster, the evidence, and a tailored pitch angle.
Provenance. This is a sibling of the "find businesses with a fixable weakness" pattern, specialised for app stores. It re-implements source → trigger → enrichment → qualification → prospect record → pitch angle on official Apify actors. The app-version tag is what makes this variant distinct — see The trigger logic. Where the literal source couldn't be followed, see Deviations.
- How it works
- The trigger logic
- Who this is for
- Strengths
- Limits & failure modes
- Inputs and outputs
- Example: the synthetic sample
- Quick start
- Configuration
- Compliance & data handling
- Cost notes
- Deviations from the source
- License
config.yaml (app ids, thresholds — no app/publisher in code)
│
▼
[1] Source official Apify actors ── reviews tagged with APP VERSION
│ Google Play · Apple App Store (or offline `mock`)
▼
[2] Normalize each actor's shape -> canonical App/Review records
▼
[3] Bucket group reviews by version; pick the newest release (by recency)
▼
[4] Triggers regression · defect_cluster · unanswered (all anchored to the new version)
▼
[5] Qualify clears volume floors AND >=1 allowed signal fired?
▼
[6] Analyse name the regression + a pitch angle (deterministic | LLM)
▼
prospects.csv + dropped.csv + report.md
The app-version tag is the whole point — it lets the tool attribute a complaint to a specific release, which is both a sharper signal and a more actionable pitch than "this app has some bad reviews."
| Signal | What it detects |
|---|---|
| regression | Negative-review share on the newest version exceeds the older baseline by ≥ regression_jump percentage points |
| defect_cluster | The same defect word/phrase (crash, "since the update", login, freeze…) across ≥ N negative reviews on that new version |
| unanswered | ≥ X% of the new version's negative reviews have no developer reply |
The "newest version" is chosen by most-recent review activity (no fragile version-string parsing). An app qualifies if it clears min_reviews / min_reviews_per_version / baseline_min_reviews and at least one allowed signal fires.
The seller is whoever prevents or fixes bad releases:
- Mobile crash / observability vendors (Sentry / Instabug-class).
- QA & test-automation, mobile CI/CD, release-gate tooling.
- App-support-automation and review-response vendors (→ the
unansweredsignal). - Mobile localization (locale-specific defect clusters) and mobile dev agencies.
- Or a competitor app running displacement while a rival stumbles.
The pitch names the version and the defect: "v4.0 shipped a crash your users are screaming about and you haven't replied — here's a QA gate / crash diagnostics."
- Sharpest timing on the review-mining landscape. The version tag makes causality legible and the pitch land within days of a bad release.
- Clean entity resolution. App ids are exact — no fuzzy business matching.
- High-adoption, high-success actors that return per-review version + date + developer-reply — exactly the fields the signals need.
- A rating dip isn't always a fixable regression. A pricing/policy change, a review-bombing campaign, or seasonality can all depress a new version's ratings. The defect-cluster signal is what separates a real regression from noise — keep it in
require_signalsand tunedefect_keywords, or expect false positives. - Apple App Store coverage is capped. The App Store actor reads Apple's public RSS feed, which returns at most ~500 reviews per app. Fine for a recent-release velocity read; not a full history. Google Play is the richer source.
- Small/long-tail apps lack the volume to compute a per-version comparison; the per-version and baseline floors deliberately drop them rather than fire on noise.
- Version attribution depends on the actor. If reviews come back without a version field, the regression/defect/unanswered signals (all version-anchored) can't fire. See docs/SETUP.md → "Field mappings".
- The publisher can be hard to reach as a named buyer versus an app-store listing — this tool finds the app, not the decision-maker's inbox.
Input: a config.yaml — which stores, which app ids (Google Play package names; App Store numeric ids or URLs), and the thresholds.
Outputs:
prospects.csv— one row per qualifying app:app, platform, app_id, url, review_count, regressed_version, signals, weakness, pitch_angle, evidence.dropped.csv— every app considered and not listed, with the reason.report.md— counts, per-platform and per-signal tallies, config in effect.
Synthetic fixtures under sample/ (invented apps — com.fiction.notesapp, Fiction Weather; no real data). With the offline mock transport (no token, no spend):
make sample
# → apps=2 evaluated=2 qualified=1
com.fiction.notesapp qualifies: its v4.0.0 negative share is 87% vs 0% on v3.2.0 (+87pp), with a crash / since update / login defect cluster and 85% of the new-version negatives unanswered. Fiction Weather (all one version, high ratings) is dropped — "no regression signal fired." See out/prospects.csv.
Prerequisites: Python ≥ 3.9. An Apify token is needed only for a real run. Full walkthrough: docs/SETUP.md.
python3 -m venv .venv && . .venv/bin/activate
pip install -r requirements.txt
make sample # offline; no token, no spend
cat out/report.md
cp config.example.yaml config.yaml # add app ids + thresholds
cp .env.example .env # put APIFY_TOKEN here (git-ignored)
python -m radar --config config.yamlEverything is config, never code — see the fully-commented config.example.yaml. Key knobs:
| Setting | What it controls |
|---|---|
transport |
apify (real) or mock (offline fixtures) |
source.google_play.app_ids |
Package names (e.g. com.example.app) or Play URLs |
source.app_store.app_ids |
Numeric app ids or App Store URLs |
source.<platform>.country / language |
Storefront + review language |
source.<platform>.max_reviews |
Reviews to pull per app (cost lever) |
triggers.regression_jump |
Percentage-point jump that counts as a regression |
triggers.defect_min_cluster / defect_keywords |
Recurring-defect detection |
triggers.min_reviews* |
Volume floors (total / per-version / baseline) |
triggers.require_signals |
Which signals may qualify an app |
analysis.mode |
deterministic (offline) or llm (Claude) |
output.redact |
Hash app names + drop ids/URLs for anything shared |
Secrets live in .env (git-ignored). Copy .env.example.
- Platform terms & rate limits. The actors read Google Play / Apple App Store public review pages/feeds; those platforms have terms governing automated access, and the actors are third-party tools, not sanctioned integrations. Use within each platform's terms and Apify's, and keep volumes proportionate.
- Personal data (GDPR / CCPA). Reviews carry reviewer usernames — lighter than named individuals, but still personal data. The tool aggregates to the app level and doesn't need reviewer identity; prefer not to retain usernames, and mind the review text you keep as evidence.
- Don't label apps publicly. A "regressed release" list is for your outreach, not publication.
output.redact: truehashes app names and drops ids/URLs for anything you share. - Not permission to contact. A qualified row is a signal, not consent — your outreach compliance is your responsibility.
Free tool; you pay Apify per review pulled (and Anthropic only in LLM mode). Full breakdown with the formula and small/medium/large estimates: docs/COSTS.md. Actor existence and pricing model verified against the live Apify Store API (2026-09); dollar figures are estimates — confirm current pricing. Cost formula: apps × max_reviews × per-review-rate (+ LLM tokens on qualified apps).
- Re-platformed onto Apify (the source used a self-hosted scraper stack). The trigger/qualification/pitch steps are faithful; the app-store version anchor is the specialization.
- LLM analysis is optional, not mandatory — deterministic by default (no key, offline, reproducible).
- No outreach/sequencing — this produces the qualified list and pitch angle; sending is yours.
- Field mappings are verified against a live Google Play
reviews-mode run (2026-09): per-reviewscore,text,version,date,replyText,userName. App Store mappings are tolerant of key renames; adjust inradar/sources.pyif an actor's output diverges.
Apache-2.0 (see LICENSE, NOTICE). This project invokes third-party Apify actors and (optionally) the Anthropic API at arm's length; it does not bundle or modify them. Apple, Google, and the app publishers whose reviews the actors read are not affiliated with this project.