Review Monitor: Negative Review Alerts and Rating Drift
Pricing
from $12.00 / 1,000 entity checkeds
Review Monitor: Negative Review Alerts and Rating Drift
New 1-2★ reviews, rating drift, owner-reply rate and review velocity per watched business, app or SaaS product since your last run; $12 per 1,000 checks. Alerts $2 per 1,000. 11 sources incl. Booking, Tripadvisor, Google Maps, Yelp, app stores, Capterra. No reviewer names.
Pricing
from $12.00 / 1,000 entity checkeds
Rating
0.0
(0)
Developer
Arcadium Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Review Monitor: New Negative Reviews and Rating Drift
Give it a watchlist of hotels, restaurants and local businesses, apps or SaaS products (their page URLs on Booking.com, Agoda, Trip.com, Tripadvisor, Google Maps, Google Hotels, Yelp, Google Play, the App Store, Capterra or TrustRadius) and every run tells you what changed since the last run. You get one alert row for each new 1★ or 2★ review and one summary row per entity with rating drift, the 30-day average against the lifetime average, owner-reply rate and reply lag, review velocity and, for apps, a per-version rating regression.
All sources come back in one feed with one row shape, deduplicated, with no reviewer names or avatars. Schedule it daily and connect a webhook, Slack or a sheet through Apify integrations.
It is for hotel groups and franchises watching many locations, agencies reporting on clients' reputation, app teams who need to know when a release hurts the rating, and product marketers tracking their SaaS product next to competitors.
Use with AI agents
Returns new negative reviews and reputation metrics (rating drift, 30-day vs lifetime average, owner-reply rate and lag, velocity, app-version regressions) per watched URL since the previous run. Agents can call it through the Apify MCP server (https://mcp.apify.com, open source as apify/actors-mcp-server) as arcadium/reputation-monitor: the input schema is read from the actor, the result is a dataset of JSON rows.
Input example (minimal):
{"startUrls": ["https://www.booking.com/hotel/gb/the-savoy.html","https://www.yelp.com/biz/katzs-delicatessen-new-york","https://apps.apple.com/us/app/slack/id618783545"],"stateKey": "my-watchlist"}
What comes back: one summary row per entity and one alert row per new review rated at or below alertMaxRating (default 2) since the last run with the same stateKey. Filter on the rowType field; flags on the summary row lists what needs attention (new_negative, rating_drop, recent_below_lifetime, version_regression, unanswered_negative, velocity_spike). The first run per entity is a baseline: it records the newest reviews as history and returns only the summary (status: "baseline") at the cheaper baseline price; alerts start with the second run. A URL that fails gets a free error row with a code (invalid_input, not_found, blocked, unavailable or no_match), a message and the input. Retry on blocked and unavailable later; do not retry the other codes with the same input.
Cost of a daily check of 100 entities with 3 new negative reviews in total: 100 checks at $12 per 1,000 plus 3 alerts at $2 per 1,000 is about $1.21 per run on the free plan. The first run of the same list costs about $0.90 (baseline). Error rows are never charged.
What you get
Summary row, one per entity and run:
| Field | What it holds |
|---|---|
status | baseline (first run), changed (new or edited reviews) or unchanged. |
rating, previousRating, ratingChange | The source's average now, at the previous run, and the drift in stars. |
reviewCount, reviewCountChange | Stated review count (app stores: star ratings) and how much it grew. |
newReviews, newNegativeReviews, newReviewsAvgRating | Reviews first seen this run, how many became alerts, and their average. |
avgRating30d, avgRatingLifetime, rating30dVsLifetime | Recent vs usual. Negative = recent reviews are worse. |
reviews7d, reviews30d, reviewsPrior30d, reviewsPerWeek | Velocity. |
ownerReplyRate90d, medianReplyLagDays, unansweredNegative30d | Reply rate of the last 90 days, median days to reply, negative reviews still without a reply. |
appVersions, versionRegression | Apps: rating per version, and the newest version when it averages 0.5+ stars below the one before. |
windowComplete30d | true when the remembered history reaches back 30 days, so the 30-day figures are exact. |
flags | What needs attention (empty on a baseline). |
Output example:
{"rowType": "summary","status": "changed","platform": "appstore","entityId": "618783545-us","input": "https://apps.apple.com/us/app/slack/id618783545","name": "Slack","url": "https://apps.apple.com/us/app/slack/id618783545","checkedAt": "2026-10-21T22:59:28.653Z","previousCheckAt": "2026-10-21T22:54:50.343Z","rating": 4.14,"previousRating": 4.14,"ratingChange": 0,"reviewCount": 54143,"previousReviewCount": 54115,"reviewCountChange": 28,"newReviews": 6,"editedReviews": 0,"newNegativeReviews": 1,"newReviewsAvgRating": 4.5,"avgRating30d": 3.29,"previousAvgRating30d": 3.29,"reviews7d": 55,"reviews30d": 100,"reviewsPerWeek": 23.3,"avgRatingLifetime": 3.29,"lifetimeSource": "sample","rating30dVsLifetime": 0,"ownerReplyRate90d": 0.05,"medianReplyLagDays": 0,"unansweredNegative30d": 35,"appVersions": [],"versionRegression": null,"windowComplete30d": false,"historyReviews": 100,"historySince": "2026-10-08","stopReason": "onlyNew","flags": ["new_negative", "unanswered_negative"]}
Alert row, one per new negative review:
{"rowType": "alert","platform": "appstore","entityId": "618783545-us","entityName": "Slack","entityUrl": "https://apps.apple.com/us/app/slack/id618783545","reviewId": "AppStore___14688767912","date": "2026-10-20","rating": 2,"title": "Issue","text": "A lot of issues when bringing up certain things they don't bring them up in order unless you put that you want newest. ...","appVersion": null,"ownerReplied": true,"ownerReply": "Ah, apologies, we're working to tackle this bug. ...","changeType": "new","checkedAt": "2026-10-21T22:59:28.653Z"}
changeType: "edited" marks a review you already knew that its author edited down into the alert band.
Input
| Field | What it does |
|---|---|
startUrls | One URL per entity: Booking.com, Agoda or Trip.com hotel, Tripadvisor hotel/restaurant/attraction, Google Hotels hotel, Google Maps place (restaurant, shop, clinic, any business), Yelp business, Google Play app, App Store app, Capterra product, TrustRadius product. Up to 2,000. |
stateKey | Names the watchlist. Each run is compared with the last run of the same name. A new name starts a new baseline. |
alertMaxRating | Alert at or below this many stars (1-4, default 2). Booking, Agoda, Trip.com and TrustRadius 1-10 scores are halved first. |
maxReviewsPerEntity | Newest reviews read per entity and run, default 100. Later runs stop at the last review seen, usually after one page. |
language, country | Google Play review language (default en) and app store country (default us). |
includeErrorRows | Free error rows, on by default. |
How the comparison works
The actor keeps two small records per entity and watchlist in your account: the ids of the reviews it has seen (reputation-monitor-seen) and a history of review facts (date, rating, replied, reply date, app version; no text) in reputation-monitor-state. Each run reads the newest reviews until it reaches ones it has seen, writes the rows and saves the new state.
- The first run per entity and watchlist only records the history: no alerts, cheaper price.
- A reply that arrives days after the review is picked up on the next run that still reads that review (the run re-reads the last 7 days).
- If a run stops early (your maximum cost per run), the reviews that were not alerted stay unseen and come with the next run.
- The same entity under two URLs is checked once, and each review is reported once.
Alerts: webhook, Slack, sheets
Use Apify's own integrations on a schedule: add a webhook for "Run succeeded" that posts the dataset link to your endpoint, or use the Slack, Google Sheets, Zapier or Make integrations. Filter on rowType: "alert", or on summary rows whose flags are not empty.
Use it from an AI assistant
Use the Apify actor "arcadium/reputation-monitor" to watch these places and apps.Input: {"startUrls":["https://www.booking.com/hotel/gb/the-savoy.html","https://www.yelp.com/biz/katzs-delicatessen-new-york"],"stateKey":"daily"}Rows with rowType "alert" are new 1-2 star reviews. Rows with rowType "summary" have rating, ratingChange,avgRating30d, ownerReplyRate90d and flags per entity. The first run is a baseline with no alerts.Run it, then summarise what went wrong in the new negative reviews and which entity needs a reply first.
Use it from code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("arcadium/reputation-monitor").call(run_input={"startUrls": ["https://www.booking.com/hotel/gb/the-savoy.html"],"stateKey": "daily",})for row in client.dataset(run["defaultDatasetId"]).iterate_items():if row["rowType"] == "alert":print(row["entityName"], row["rating"], row["text"][:80])
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('arcadium/reputation-monitor').call({startUrls: ['https://apps.apple.com/us/app/slack/id618783545'],stateKey: 'daily',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items.filter((i) => i.rowType === 'summary' && i.flags.length > 0));
Pricing
You pay per entity checked and per alert row, at the price of your Apify plan. The first run per entity is a cheaper baseline. Error rows are free.
| Apify plan | Per 1,000 entity checks | Per 1,000 alerts | Per 1,000 baselines |
|---|---|---|---|
| Free | $12.00 | $2.00 | $9.00 |
| Starter (Bronze) | $12.00 | $2.00 | $9.00 |
| Scale (Silver) | $12.00 | $2.00 | $9.00 |
| Business (Gold) | $12.00 | $2.00 | $9.00 |
| Platinum | $12.00 | $2.00 | $9.00 |
| Diamond | $12.00 | $2.00 | $9.00 |
Plus a one-off $0.001 per GB of memory when a run starts.
Examples: one hotel checked daily for a month is 30 checks, $0.36 plus a few cents of alerts. 50 locations checked daily is about $18 a month. Set a maximum cost per run in the Apify run options and the actor stops before it goes over.
Use cases
- Hotel groups and franchises: one feed for every location on Booking, Tripadvisor and Yelp; reply to the new 1★ reviews first, watch
ownerReplyRate90dper location. - Agencies: a weekly reputation report per client from the summary rows (
ratingChange,rating30dVsLifetime,reviewCountChange). - App teams:
versionRegressionshows when a release averages clearly lower than the one before it; alerts carry theappVersion. - SaaS product marketing: watch your Capterra and TrustRadius pages and three competitors' in one watchlist.
Limitations
- Public review pages only. Nothing behind a login.
- Supported: Booking.com, Agoda and Trip.com hotels (Agoda: its own reviews, not the Booking.com ones it also shows), Tripadvisor places, Google Hotels hotels and Google Maps places (Google's own reviews; dates are approximate because Google shows "3 months ago"), Yelp businesses, Google Play and App Store apps, Capterra and TrustRadius products. Other URLs return a free
invalid_inputerror row. - The 30-day, reply and velocity figures come from the reviews the actor has read. For entities with more reviews per month than
maxReviewsPerEntitythey describe the newest reviews read (windowComplete30d: false) until the history fills up over later runs. - App stores state the average of all star ratings, most of them without text, so
avgRatingLifetimethere is the average of the written reviews read (lifetimeSource: "sample"). - Reply lag needs a reply date: Tripadvisor, Agoda, Trip.com, Yelp and the app stores have one; Booking and Capterra show the reply only; TrustRadius has no owner replies.
- App versions: Google Play states the version on every review. The App Store does so only on its RSS feed, which the actor uses as a fallback, so App Store version data is usually empty.
- TrustRadius is read through residential proxies; the actor reads at most 3 pages (30 reviews) per TrustRadius product and run.
- A review that is deleted on the site is not reported.
- Entities are read one after another, about 5 seconds each (7 on the first run), so 700 entities take about an hour. The default run timeout is 4 hours; split bigger watchlists into several
stateKeys run in parallel.
Privacy
Reviewer names, avatars, profile links and similar fields are never collected. Email addresses and phone numbers inside review text are replaced by [email] and [phone], and names in greetings ("Dear John") are removed. The saved history holds only review ids, dates, ratings, reply status and app versions.
FAQ
Why did the first run return no alerts? It is the baseline: there is nothing to compare with yet. It records the newest reviews per entity and returns one summary row each at the baseline price. Run it again tomorrow (or put it on a schedule) and the alerts come.
How do I start over?
Use a new stateKey. The old records stay in the reputation-monitor-state and reputation-monitor-seen stores until you delete them.
Can I get every new review, not only the negative ones?
Set alertMaxRating to 4 for everything below 5 stars, or use the per-site review actors with "only new reviews" for full review rows.
Something looks wrong in the output. Who do I tell? Open an issue on the actor page with the run link. We read them all.