Sort by lowest rating, take one page per business, then filter on the review's last-edit date rather than its posted date. Across 60 Phoenix business profiles pulled through Scavio's /api/v2/google/maps/reviews, sort_by=lowest_rating found every profile carrying a 1- or 2-star review from the last six months, 46 of 46, at one page and one credit each. sort_by=newest found 70% of them and would have cost roughly fourteen times more to close the gap.
That is the whole method. The rest of this post is why the two obvious alternatives fail, and the one field that quietly ruins the result if you read the wrong one.
No Google reviews API filters by star rating
This is the constraint everything else follows from. Every reviews endpoint on the market gives you a sort, not a filter. Scavio's /api/v2/google/maps/reviews accepts sort_by of relevance, newest, highest_rating or lowest_rating, and no rating or date parameter at all. SerpApi's published parameter list for the same job is data_id, place_id, hl, sort_by, topic_id, query, num and next_page_token, with sort values qualityScore, newestFirst, ratingHigh and ratingLow. Same shape, same gap.
The consequence is that "businesses with a 1-star in the last six months" cannot be expressed as a query. You pull reviews and you filter them yourself, and the only decision that is actually yours is which sort you pay for. That decision turns out to matter a lot.
The trap in the tooling built on top is worth knowing too: the wrappers that do expose a date cutoff generally implement it by forcing the sort to newest. So the one control that sounds like it solves this problem takes away the sort that does.
What we measured
Four local-service verticals in one metro, because these are the verticals reputation and lead-gen work actually targets: auto repair, dentist, HVAC contractor, moving company. Top 15 results each from /api/v2/google/maps/search with map center @33.4484,-112.0740,12z, giving 60 distinct business profiles with a median of 578 lifetime reviews.
For each profile we pulled one page of 20 reviews two ways, sort_by=lowest_rating and sort_by=newest. That is 2,280 review objects for 124 credits, or $1.24, captured 2026-08-28. A profile counts as qualified if it carries at least one 1- or 2-star review inside 183 days.
curl -X POST https://api.scavio.dev/api/v2/google/maps/reviews \
-H "Authorization: Bearer $SCAVIO_API_KEY" \
-H "Content-Type: application/json" \
-d '{"data_id":"0x872b1268adc9c6a5:0x3d84be39dcfdfa4","sort_by":"lowest_rating","num":20}'Lowest rating wins on recall and on cost at the same time
The result that surprised us: the cheap method is also the complete one.
sort_by=lowest_rating | sort_by=newest | |
|---|---|---|
| Qualified profiles found | 46 of 46 (100%) | 32 of 46 (70%) |
| Pages fetched | 60 | 60 |
| Median history on page 1 | all-time | 68 days |
| Page 1 covers a full 183 days | always | 12 of 60 profiles |
| Pages to cover 183 days | 1 | 3 median, 183 worst case |
| Estimated credits for the window | 60 | about 824 |
newest is expensive for a reason that is easy to miss when you reason about it instead of measuring it. A page of 20 newest reviews spans a median of 68 days, and only 12 of the 60 profiles had a first page that reached back a full six months on its own. To actually cover the window on the newest sort you paginate until the dates fall off the end, and on the busiest profile in the sample, an HVAC contractor with 34,803 lifetime reviews whose 20 newest reviews spanned a single calendar day, that is a floor of roughly 180 pages for one business. Sorting by lowest rating puts every bad review this business has ever received on page 1 by construction.
The recall gap is the same effect seen from the other side. newest missed 14 qualified profiles not because their bad reviews were old, but because their good reviews were numerous enough to push the bad ones off page 1.
But 72% of what comes back is stale
Recall is not the same as an answer. Page 1 of the lowest-rating sort is sorted by rating, and nothing else, so what you get is a business's worst reviews across its entire history.
Median age 1.4 years. The oldest review returned as a current result was 14.6 years old. So the sort buys you completeness for one credit and hands you the date filter as homework, which is fine, as long as you know the filter is not optional. A list built by taking the lowest-rating page at face value is a list of businesses that had a bad month in 2019.
Two of the 60 profiles returned a page of nothing but 5-star reviews. That is not an error and it is the single cleanest signal in the dataset: an ascending sort that opens at five stars means the profile has no low reviews at all. Those are the two businesses to drop first.
The field that ruins the filter
Google's review objects carry two timestamps and they are not interchangeable.
iso_dateis when the review was posted.iso_date_of_last_editis when it was last changed, and it is the key the newest sort orders by, and it is what the human-readabledatestring reflects.
Here is a real 1-star review from an HVAC contractor in the sample:
{
"rating": 1,
"date": "Edited a day ago",
"iso_date": "2020-08-06T22:30:12Z",
"iso_date_of_last_edit": "2026-08-27T18:25:24Z"
}Filter that on iso_date and you discard it as a six-year-old complaint. Google displays it as a day old. A customer who came back after six years to sharpen a 1-star review is not stale intent, and the profile owner is looking at it right now.
This is not an edge case. 15.8% of the 2,280 reviews we captured carried a last edit at least a day after posting, with a median gap of 389 days and a longest of 12.1 years. Within the sample, 23 low-star reviews sit inside the six-month window by last-edit date and outside it by posted date, and one of the 46 qualified profiles is only visible at all if you read the edit field. Read iso_date_of_last_edit, and fall back to iso_date when it is absent.
Serial one-star reviewers are real, and smaller than you think
The standard objection to any list built this way is that a meaningful share of 1-stars come from accounts that leave 1-stars everywhere, so the business is not a lead, the reviewer is just a reviewer. The reviews carry enough identity to test that: each one has a user.link, a user.name and a user.reviews lifetime count.
Across the 60 profiles there were 891 distinct low-star reviewer identities, and 57 of them, 6.4%, appeared on more than one of the 60. Median lifetime review count was 8 for authors of 1- and 2-star reviews against 5 for authors of 5-star reviews. So the effect is real, it is worth a dedup pass keyed on user.link once your own crawl is large enough for repeats to show up, and it is nowhere near the dominant source of noise. Staleness is, by roughly an order of magnitude.
Where this breaks
One metro, four verticals, 60 profiles, page 1 only. The 100% recall figure is measured against qualified profiles as identified by either method on page 1, so it is a statement about which sort surfaces more, not a proof that a lowest-rating first page is exhaustive for a business with thousands of 1-stars. Deleted reviews are gone from both sorts, so neither method reconstructs history. And the 6.4% repeat-reviewer rate is only within these 60 profiles; the true rate across a metro is higher and you will only see it once you have crawled enough of one.
Worth flagging separately: one of the SERPs we pulled while researching this returned an organic result at position 3 as a raw google.com/goto token rather than a destination URL. When we measured the goto rollout yesterday, every organic token in that capture sat at position 1 or 2, so this one falls outside the band we had observed. If you are joining review data back to organic positions, resolve those tokens before you store them.
The recipe
Three calls and a filter.
/api/v2/google/maps/searchwith your vertical and anllmap center, for thedata_idof each business. 1 credit per 20 results./api/v2/google/maps/reviewsper business withsort_by=lowest_ratingandnum=20. 1 credit each.- Keep reviews where
rating <= 2andiso_date_of_last_editis inside your window. Drop profiles whose page opens at 5 stars. - Dedup on
user.linkacross your whole crawl, not per business.
At 500 businesses that is 25 search calls plus 500 review calls, so 525 credits, $5.25 for a metro-wide pass. Re-running it weekly to catch new complaints is another $5.25 a week, because reviews you already have do not need to be fetched twice.
FAQ
How do I see the 1-star reviews on a Google Business Profile?
In the Maps interface you sort the reviews panel by lowest rating. Programmatically it is the same operation: Scavio's /api/v2/google/maps/reviews with sort_by=lowest_rating returns them worst-first, 20 per page, for 1 credit. There is no parameter that returns only 1-stars, so you filter on rating in your own code.
How do I extract Google reviews to a spreadsheet?
Call the reviews endpoint per business and flatten the reviews array. The fields worth keeping are rating, iso_date_of_last_edit, snippet, user.name, user.link and user.reviews. place_info on the same response carries the business title, address, rating and lifetime reviews count, so one call gives you both the row and its parent record.
Can I filter Google reviews by date range through an API?
No public Google reviews API exposes a date filter, and neither does any of the third-party endpoints we checked. You get sort order and pagination. The practical approach is to sort by lowest rating, which is bounded at one page per business, and apply your date window client-side rather than sorting by newest and paginating until the dates run out.
Why do some reviews show a date that does not match iso_date?
Because the display string tracks edits. A review posted in 2020 and edited yesterday reads "Edited a day ago" while iso_date still says 2020. Use iso_date_of_last_edit for anything recency-based, and treat iso_date as the creation timestamp it is.
Is scraping Google reviews legal?
Reading public review data through a search API is a different act from scraping logged-in surfaces or circumventing access controls, and it is the route we build on. The review text is still the author's, and republishing it verbatim carries its own obligations separate from how you obtained it. Our longer treatment is in Google Reviews API: legal considerations and alternatives.
Can a business get rid of a bad Google review?
Only by reporting it against Google's content policies or persuading the author to remove it, and neither is reliable. That unreliability is exactly why recent negative reviews are a usable outreach signal: the owner cannot make it go away, so the problem is still theirs when you call.
What this leaves you owning
You now own a weekly job that resolves 500 data_id values, pulls a review page for each, tracks which complaints you have already seen so a re-run does not re-alert on a two-month-old 1-star, and reads the correct one of two nearly identical date fields. When Google changes a response shape, or the review panel starts paginating differently, that job breaks quietly and your lead list simply gets shorter, which is the failure mode nobody notices for a month.
Scavio absorbs the part that changes. Proxy rotation, the map-center handling that decides which businesses you even see, and normalizing Google's review objects into the same shape run on our side, so a layout change upstream is our on-call rather than your Monday. What stays yours is the filter, which is four lines and does not rot.
Pricing is 1 credit per call, $0.01 per credit, no monthly commitment. The metro-wide pass above is $5.25. The 60-profile run behind every number in this post cost $1.24.
Start with 50 free credits, no card required — enough to run one vertical in one city and check the 72% staleness figure against your own market before you spend anything.
Endpoint reference: Place Reviews and Maps Search.