App Release Complaint Regression Monitor avatar

App Release Complaint Regression Monitor

Pricing

from $500.00 / 1,000 app analyses

Go to Apify Store
App Release Complaint Regression Monitor

App Release Complaint Regression Monitor

Compare public complaint rates before and after an app release, with denominators, sample sizes, and short review examples. A rate change is a time-window association, not proof of cause.

Pricing

from $500.00 / 1,000 app analyses

Rating

0.0

(0)

Developer

Olalekan Johnson

Olalekan Johnson

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Share

App Release Complaint Regression Monitor (Google Play)

Unofficial monitor for mobile product teams and app agencies. Give it public app ids, languages, and countries. It collects new and edited reviews, clusters complaints, and compares rates across a before window and an after window.

A row looks like login complaints 3/150 to 18/160. The denominator is the number of reviews in that window. An alert is emitted only when both windows are covered, both samples meet the minimum size, and both the percentage-point increase and the relative increase are met. The comparison is a time-window or version association, not causation. A higher rate after a date or version does not prove the release caused the complaints.

Unofficial, and not affiliated with Google or Apple. Public review text stays the reviewer's. This Actor keeps a short excerpt for your investigation and redacts email addresses and phone numbers. It does not store profile images or profile links.

Contact: plusonecares@gmail.com

Who it is for

Teams that already read reviews after a release and need a rate they can check, with the reviews that produced it. It is a poor fit for a low-volume app that never reaches the sample minimum, and it will say so instead of inventing an alert.

What you provide

FieldDefaultRole
appIdscom.whatsappGoogle Play package names
languagesenPlay language code, such as en or pt-BR
countriesusStorefront, such as us
lookbackDays7Timestamp window to compare. Split into two equal halves unless you set a release
maxReviewsPerScope200Newest-first cap per app, language, and country. Maximum 1000
publicationLagDays2Extra days re-read every run so a late-appearing review can still be seen
releaseDateemptyYYYY-MM-DD. Splits every app that has no per-app override
appVersionemptyExact version string. Used only when the store put that version on the review
releasesemptyOptional [{ "appId", "version", "releasedAt" }]
customCategoriesemptyOptional [{ "name": "notifications", "keywords": ["notification", "push"] }]
minSampleSize40Minimum reviews in each window
minRateIncreasePercentagePoints5Minimum rise in the complaint share
minRelativeIncrease11 means the later rate is at least double the earlier rate. A jump from zero uses the percentage-point rule
minComplaintCount5Minimum complaints on the later side
maxComplaintScore3Stars at or below this count as complaints when a keyword hits. Higher stars count only when the text also describes a failure
alertOnlyOnChangefalseSkip a repeat alert when both rates stayed within one percentage point of the previous run
outputModefullfull, alerts-only, or changes-only
appleAppIdsemptyOptional numeric App Store ids
enableLlm / llmApiKeyoffOptional labels for unmatched low-star reviews, on your own key
stateStoreNameapp-release-complaint-stateNamed store for review hashes and previous alerts

At most 12 app-language-country scopes run per execution. Further scopes are reported as unsupported and are not charged.

Built-in categories: login, crash, payments, performance, ads, ui, sync. English keywords always apply. Spanish, Portuguese, German, and French add a short extra list when the language code matches. Other languages still collect reviews. Add customCategories, or turn on the optional model, when the built-in list is the wrong vocabulary.

What you get

The default dataset is flat, so a webhook, Make, n8n, or a spreadsheet can filter it without unpacking nested objects. Every row has recordType, sourceUrl, observedAt, coverageStatus, and associationNote.

recordTypeWhen it appears
reviewA new or edited review that was delivered. Unchanged reviews are not written again
category-rateOne row per category, with both counts and both denominators
regression-alertThe thresholds and sample sizes were met
regression-clearedalertOnlyOnChange is on and a previous alert no longer meets the rule
scope-summaryWhat was collected, which comparison was used, and what was charged

coverageStatus is one of complete, partial, empty, blocked, or unsupported. Those are different outcomes. A finished run can still be partial.

comparisonBasis is one of:

  • calendar-halves — the lookback split into two equal periods
  • release-date — equal-length periods before and after the date you supplied
  • app-version — the version you named versus other reviews that carry a version. Reviews with no version are excluded and counted in versionUnknownReviews
  • sampled-halves — the cap was hit before the requested window was covered. Rates describe the older half and the newer half of the sample. Alerts are withheld

The run summary is in the default key-value store under OUTPUT. It includes counts, chargedUnits, deliveredUnits, and per-scope coverage. Review baselines live in the named state store, not in OUTPUT.

Example alert

{
"recordType": "regression-alert",
"recordId": "alert:google-play__com.example.app__en__us:login:3-18",
"source": "google-play",
"appId": "com.example.app",
"language": "en",
"country": "us",
"category": "login",
"ratePhrase": "login complaints 3/150 to 18/160",
"beforeCount": 3,
"beforeSample": 150,
"afterCount": 18,
"afterSample": 160,
"beforeRate": 0.02,
"afterRate": 0.1125,
"direction": "up",
"regressionDetected": true,
"alertEmitted": true,
"comparisonBasis": "calendar-halves",
"coverageStatus": "complete",
"windowsComplete": true,
"examplesText": "Cannot log in after the update || Login fails on the password screen",
"sourceUrl": "https://play.google.com/store/apps/details?id=com.example.app&hl=en&gl=us",
"associationNote": "Time-window or version association, not causation. A higher complaint rate after a date or app version does not prove the release caused the complaints."
}

A 5-star review that says login is smooth is a mention, not a complaint, so it stays in the denominator and out of the numerator. A 1-star review that matches no keyword is counted in unclassifiedComplaintCount on the scope summary so you can see what the lexicon missed.

Pricing

This Actor uses pay-per-event pricing. You are charged only for a unit it is about to deliver, and there is no separate per-result charge. Each run also has a $0.005 start event. Default memory is 512 MB (maximum 1024 MB).

EventWhen it is billedPrice
review-processedOne new review, or one review whose text, stars, or version changed$0.002
app-analysisOne completed app-language-country analysis (Play), or one app-country analysis (App Store)$0.50

An unchanged review is not billed again. A blocked or unusable collection is not billed, and it does not replace a saved baseline. If the run spend limit is reached, delivery stops. Reviews that were not delivered are not marked as done, so a later run can still process them.

WorkloadEventsCost
Default first look: WhatsApp, English, United States, 200 new reviews, 1 analysis200 × $0.002 + 1 × $0.50$0.90
Same scope the next morning, 30 new reviews30 × $0.002 + 1 × $0.50$0.56
Eight apps, one language, weekly, about 2,000 new reviews2,000 × $0.002 + 8 × $0.50$8.00 per run
Larger month: 20,000 new or edited reviews and 60 analyses20,000 × $0.002 + 60 × $0.50$70.00

The cap inside one run is 12 scopes and 1,000 reviews each: 12,000 × $0.002 + 12 × $0.50 = $30 before the platform start event. Set a max total charge on the run if you want a harder ceiling. Optional model calls are billed by your model provider, not by these events.

Local runs and accounts that are not on pay-per-event pricing still receive the dataset. chargedUnits stays at zero in that case. deliveredUnits shows what would be billed once the events are priced.

Default input, checked 23 October 2026

com.whatsapp, language en, country us, 7-day lookback, cap 200. The run finished in a few seconds and wrote 208 rows: 200 reviews, 7 category-rate rows, and 1 scope summary.

Those 200 newest reviews spanned about 2 hours, so the 7-day window was partial and the basis was sampled-halves. One rate row was login complaints 0/100 to 1/100. No regression alert was emitted, because a cut-off sample is not allowed to alert. A second run on the same baseline wrote 8 rows (the rates and the summary) and did not deliver the 200 reviews again.

That is the honest result for a very high-volume app at this cap. To cover a real release window, raise maxReviewsPerScope, narrow the lookback, or pass releaseDate / appVersion and accept partial until the read reaches the earlier side.

How a scheduled run behaves

Each successful scope stores review ids and content hashes in the named key-value store. The next run re-reads the lookback plus publicationLagDays, so a review that shows up late can still be clustered. Edits change the hash and are delivered once as edited. A run that is blocked, or that returns nothing while a baseline already exists, leaves the baseline in place.

alertOnlyOnChange compares the new rates with the previous alert. A move smaller than one percentage point does not emit another regression-alert. If the category no longer qualifies, the dataset gets regression-cleared.

Sources and limits

Google Play reviews come from the public Play batch endpoint, the same call the google-play-scraper library wraps. Requests are throttled, retried, and sent with the User-Agent app-release-complaint-monitor/1.0 (+mailto:plusonecares@gmail.com). No browser is used.

Apple is optional. The public customer-review RSS feed is storefront-scoped, about 10 pages, and sometimes returns an empty page in the middle. Empty pages are skipped. The feed is not a language feed: one analysis is charged per Apple app and country, not once per language.

Known limits:

  • The Play batch shape can change. A broken response is blocked, not an empty product.
  • Newest-first collection plus a few hundred reviews does not cover a week of a top app.
  • Keywords miss paraphrases and can hit an incidental word. unclassifiedComplaintCount and the excerpts are there so a person can check.
  • Some Play reviews have no version. They are left out of a version split.
  • The storefront country is not the reviewer's location.
  • A cleared or withheld alert is still a rate. It is not a statement that the release was fine.
  • Platform charge idempotency is short. The state store is what stops a later run from billing the same review hash again.
  • Review text in the dataset is an excerpt, capped and redacted. It is for your investigation, not a corpus to republish.

Optional model

Leave enableLlm off and the Actor is fully deterministic. Turn it on and supply llmApiKey if you want unmatched reviews at or below maxComplaintScore sent to an OpenAI-compatible chat/completions endpoint. At most llmMaxReviews (default 15) are sent per scope. Results are cached by text hash in the state store. A bad response, a non-HTTPS URL, or a host that resolves to a private or link-local address is skipped, and the keyword labels stand. The key is a secret input and is not written to the dataset.

Run it

pip install -r requirements.txt
python3 main.py

With no input file, the defaults above are used. To pass input locally, put INPUT.json in the working directory or start the run from the Apify console. Tests:

pip install pytest pytest-asyncio
pytest

On Apify, push this directory with the Apify CLI when you are ready. Memory for a normal run is 512 MB.

Integrations

Filter recordType = regression-alert if the webhook should page a human. Filter category-rate if you want every denominator, including the quiet categories.

  • Webhook. On the Actor task, add a webhook for the run succeeded event. Point it at your endpoint. Read ratePhrase, examplesText, coverageStatus, and associationNote.
  • n8n. HTTP Request node runs the Actor, then a second node fetches {{links.apiDefaultDatasetUrl}}/items?fields=recordType,appId,category,ratePhrase,examplesText,coverageStatus. Keep the items where recordType is regression-alert.
  • Make. Use the Apify module to run the Actor and list dataset items. Map ratePhrase into the message body.
  • Google Sheets. The dataset columns are flat. Export the run or connect the Apify Google Sheets integration. examplesText is the CSV-friendly form of the excerpts.

A useful schedule is once after you ship, then daily while the release is fresh. Pass the same releaseDate or appVersion each time so the windows stay comparable. Turn on alertOnlyOnChange when the first alert has already been seen.

FAQ

Does an alert mean the release caused the complaints? No. The note on every row says the figure is a time-window or version association. Use it to decide whether a person should open the excerpts and your own crash or login telemetry.

Why did the default WhatsApp run not alert? Two hundred newest reviews covered about two hours, not seven days. The Actor still returns the rates and labels the coverage partial. Alerts wait until both windows are actually covered and the sample minimum is met.

Why is a quiet category still a row? crash complaints 0/150 to 0/160 tells you the denominator existed and the lexicon found nothing. A missing row would look the same as a failed collection.

What if Google or Apple returns an error? That scope is blocked or unsupported. Nothing is charged for it. If a baseline already exists, it is kept. If every scope is blocked, the run fails after writing OUTPUT, so a schedule does not look successful.

Can I point the model URL at an internal network? No. Only public https hosts are accepted. Localhost, link-local, and private addresses are rejected.

Who do I write to? plusonecares@gmail.com. Include the run id, the input (without the model key), and whether coverageStatus was blocked or partial.