Bad reviews usually say exactly what broke in the last release, often a day before your crash dashboard makes it obvious. Here's the small script I use to get every new 1–2 star review from both stores, once a day, for a few cents a month.
The idea
- Ask each store only for reviews from the last day (
reviewsNewerThan), newest first, so the scraper stops paging as soon as it reaches older reviews. A daily run is tiny. - Keep only 1–2 stars.
- Post them to Slack (or print them).
The code
I use two hosted Actors I maintain on Apify (Google Play Reviews and App Store Reviews). With the Python client (pip install apify-client):
from apify_client import ApifyClient client = ApifyClient("<YOUR_APIFY_TOKEN>") common = {"countries": ["us", "gb"], "minStars": 1, "maxStars": 2, "reviewsNewerThan": "1 day", "includeAppDetails": False} play = client.actor("fjordlabs/google-play-reviews").call(run_input={ **common, "apps": ["com.spotify.music"], "sort": "newest", "languages": ["en"]}) ios = client.actor("fjordlabs/app-store-reviews").call(run_input={ **common, "apps": ["324684580"], "sort": "most_recent"}) for store, run in (("Play", play), ("iOS", ios)): for r in client.dataset(run["defaultDatasetId"]).iterate_items(): if r.get("type") != "review": continue print(f'[{store} {r["country"]}] {r["score"]}★ v{r.get("appVersion")}: {r["content"][:120]}') Swap in your own package name and App Store ID. Output looks like:
[Play us] 1★ v9.1.92: Casting to my speakers keeps disconnecting since the last update. [iOS gb] 2★ v9.1.92: Shuffle isn't random any more... Group by app version
The appVersion field is the quickest way to see whether a release made things worse:
import pandas as pd df = pd.DataFrame(client.dataset(play["defaultDatasetId"]).iterate_items()) print(df[df["type"] == "review"].groupby("appVersion")["score"].agg(["count", "mean"])) Make it a monitor
Schedule both runs daily in Apify and add the Slack or webhook integration. Reviews cost $0.0001 each, so even an app with 50 bad reviews a day costs about $0.15 a month. Reviewer names are left out by default (includeReviewerNames: false), which keeps the data free of personal details.
Disclosure: I built and maintain both scrapers. Questions and feature requests welcome in the comments. Links to Apify include my referral code.
