Google Business Profile Review Scraper
Pricing
from $0.42 / 1,000 results
Google Business Profile Review Scraper
Find recent low-star Google Business Profile reviews by search term and location, with the business's contact details on every row.
Pricing
from $0.42 / 1,000 results
Rating
0.0
(0)
Developer
String
Maintained by CommunityActor stats
0
Bookmarked
3
Total users
1
Monthly active users
19 days ago
Last modified
Categories
Share
Find every recent low-star Google review across a whole market, with the business's phone number on the same row as the complaint.
What can Google Business Profile Review Scraper do?
Give it a search term and a location and it returns each matching review as one row, carrying the review itself and the full business record it was left on.
π Sweep a whole market. plumber in Austin, TX finds every profile Google returns for that
search, up to 120 per term, and reads each one's reviews.
π Get the full review. The text with the reviewer's own line breaks, an exact ISO date rather than "3 months ago", the star rating, the photo count, and the reviewer's name and how many reviews they have written.
βοΈ Contact details on every row. Phone, website, street address, categories, coordinates and the profile's overall rating β so any single row is enough to act on.
π― Filter by rating and recency. 1-star only in the last 30 days, everything at 2-and-below this year, or every review a profile has. The filters are also the budget dials: a profile whose reviews all miss them costs nothing.
π Watch fixed profiles instead. Pass Google Maps URLs to monitor named businesses rather than search a market.
π Schedule it. reviewId and dataId are stable, so a daily run de-duplicates cleanly and each
new bad review arrives exactly once.
π« No login. No Google account, API key or cookies β it reads what a logged-out visitor sees.
Why scrape Google reviews?
- Reputation-management prospecting. A fresh 1-star review is a business in the market for help, and the row already carries the phone number to call.
- Agency churn signals. Watch your own clients' profiles and get the bad review the day it lands, rather than when they forward it.
- Competitor monitoring across a category and city, with the complaint text itself rather than a shifted star average.
- Complaint-theme analysis across a whole market segment, read from the review bodies.
- Franchise and multi-location oversight, one row per piece of bad feedback, grouped on
dataId.
How to use it
- Open the Actor and click Try for free, or add it to your Apify account.
- Put your terms in Search terms, one per line β
plumber,hvac. Set Location to the market, e.g.Austin, TX, or leave it empty if your terms already name one. - Leave Maximum review rating at
1for 1-star only, or raise it to2to catch 1- and 2-star. - Leave Within days at
180for the last six months, or shorten it to watch a current window.
- Click Start, then take the rows from the Dataset tab as JSON, CSV or Excel.
- Read the run's
SUMMARYrecord for any profile that could not be read, with its error.
To monitor a fixed set of businesses rather than a market, put their Google Maps URLs in Specific profiles and leave Search terms empty. A run needs one or the other and is rejected before it starts with neither. Save the input as a task and schedule it to get each bad review as it lands.
Output
One row per review. A business with three matching reviews returns three rows, each carrying the full business record.
The same row as JSON β the shape below is illustrative, showing the fields and their types rather than a captured run:
{"reviewId": "ChZDSUhNMG9nS0VJQ0FnSUMxbHZHWVh3EAE","rating": 1,"publishedAt": "2026-07-28T14:02:11.000Z","relativeDate": "3 weeks ago","text": "Booked a same-day callout and nobody turned up.\nNo call, no refund.","language": "en","imageCount": 2,"authorName": "Dana Whitfield","authorProfileUrl": "https://www.google.com/maps/contrib/108273645190273645190","authorReviewCount": 37,"reviewUrl": "https://www.google.com/maps/reviews/data=!4m6!14m5!1m4!2m3!1sChZDSUhNMG9nS0VJQ0FnSUMxbHZHWVh3","businessName": "Lone Star Plumbing Co","businessRating": 4.1,"phone": "(512) 309-1487","website": "https://lonestarplumbing.example.com/","address": "1200 E 6th St, Austin, TX 78702","categories": ["Plumber", "Drainage service"],"latitude": 30.267153,"longitude": -97.731339,"placeId": "ChIJLwPMoJm1RIYRetVp1EtGm10","dataId": "0x8644b59a80cc032f:0x5d9b464bd469d57a","mapsUrl": "https://www.google.com/maps/place/?q=place_id:ChIJLwPMoJm1RIYRetVp1EtGm10","matchedReviewCount": 3,"lowestRatedReviewsRead": 22,"searchTerm": "plumber","sourceUrl": "https://www.google.com/search?tbm=map&q=plumber+Austin%2C+TX","collectedAt": "2026-08-21T09:14:03.512Z"}
The run also writes a SUMMARY record holding the item count, the failure count and every profile
that could not be read, with its error.
Reliability
Both halves read Google's own JSON endpoints rather than scraping rendered markup, so every value is
exact: ratings are numbers, review dates are absolute timestamps, and the reviewer's own line breaks
survive into text.
Reviews are read worst-first, which is why one request per profile is enough. Sorted that way, a
single page carries the profile's entire low-star history rather than its most recent 50 reviews β on
a busy profile that page held all 16 one-star and all 6 two-star reviews it had ever received, and the
next page was already entirely five-star, while reading newest-first would have needed eight pages to
cover the same six months. lowestRatedReviewsRead reports how many that page held; a value of 50
means it filled up, so a profile with more than 50 reviews at or below its floor may have others this
run did not read.
A search pages up to Google's own ceiling of 120 results per term, and consecutive pages overlap, so
the distinct total per term lands nearer 100. Profiles are de-duplicated on dataId before any
reviews are fetched, so a profile that appears on two search pages is never fetched twice.
A profile whose reviews cannot be read is recorded in the run's SUMMARY under failures rather than
silently contributing nothing β an intercepted response and a profile with genuinely no matching
reviews look identical otherwise, and the difference decides whether a list is complete.
There are no retries by design: the String Unblocker owns proxy rotation and anti-bot solver selection, so a retry loop here would only re-roll the same block.
Limitations
Search and review data only: no opening hours, no menus, no photos, no popular-times data and no
"about" text. Reviews are read one worst-first page deep, so this is not a full review-history export
for a profile with more than 50 low-rated reviews. Profiles supplied by URL in places carry no
address, phone, website or coordinates, because those come from the search surface rather than the
review endpoint. Google orders a search by its own relevance rather than anything stable, so
re-running can surface a business at a different rank; de-duplicate on reviewId.
How much does it cost?
A run that returns 1,000 rows costs $0.83 on a paid plan, or $1.00 with no subscription.
You are charged per result row written to the dataset, so a run that returns nothing costs nothing,
and maxItems is the budget dial.
| Plan | Per 1,000 rows |
|---|---|
| Free | $1.00 |
| Bronze | $0.83 |
| Silver | $0.58 |
| Gold | $0.42 |
| Platinum | $0.31 |
| Diamond | $0.19 |
Apify bills its own platform usage for the run on top of this.
One row is one review, so a business with three matching reviews costs three rows. A profile whose
reviews all miss your filter contributes none and costs nothing, which makes maxReviewRating and
withinDays budget dials as much as filters.
Runs started from an Apify free plan stop at 250 requests and 250 results, and the run reports
that it reached the limit. Any paid plan runs the full input and maxItems you set.
The limit exists because this Actor fetches through our own infrastructure, which Apify does not cover for free-plan runs. It binds on requests as well as results, and one ceiling covers both the search and the review stage together, so a large input cannot spend those fetches for rows the run will not return.
Using it with the Apify API
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('usestring/google-business-reviews').call({searches: ['plumber', 'hvac'],location: 'Austin, TX',maxReviewRating: 1,withinDays: 180,maxItems: 1000});const { items } = await client.dataset(run.defaultDatasetId).listItems();const callable = items.filter((review) => review.phone !== null);console.log(`${items.length} bad reviews, ${callable.length} with a phone number attached`);
The same call works from the Python client, and from any HTTP client through the Apify REST API. The
run's SUMMARY record is in the run's default key-value store.
Related actors
- Yelp Business Scraper β the same reputation surface on the other major review platform.
- Capterra Reviews Scraper β software reviews, where the complaint text is about a product rather than a visit.
- G2 Reviews Scraper β the B2B software equivalent.
- Realtor.com Listings Scraper β local business data of a different shape, for the same territory-planning work.
Pair this with a CRM import and the phone number on every row is a call list, already filtered to businesses with a fresh complaint against them.
FAQ
How do I find businesses with recent bad reviews? Run this Actor with your search terms and
location and leave maxReviewRating at 1 and withinDays at 180. Every row is a 1-star review
from the last six months, with the business it was left on attached.
Can I widen it beyond 1-star? Yes. maxReviewRating returns any review at or below the value you
set, so 2 catches 1- and 2-star, and 5 returns everything the worst-first page held.
Does it return the review text? Yes, with the reviewer's line breaks intact. A rating-only review
with no words returns null text and keeps its rating, date and author.
Why do I get several rows for the same business? Because each row is one review. Group on
dataId, or read matchedReviewCount, which tells you how many that business had in this run.
How many businesses does one search cover? Up to 120, which is Google's own ceiling per search term; overlapping pages mean the distinct count is usually nearer 100. Use more search terms, or more specific ones, to cover a market more deeply.
Do I need a Google account, API key or cookies? No. It reads public Google Business Profiles as a logged-out visitor and never signs in.
Can I monitor specific businesses instead of searching? Yes β put their Google Maps URLs in
Specific profiles. Use the long place or review URL containing !1s0xβ¦:0xβ¦, or that 0xβ¦:0xβ¦
id on its own. A maps.app.goo.gl short link and a bare ChIJ⦠Place ID do not carry that id and
are reported in failures rather than guessed at. A profile read this way returns its reviews and
name, but phone, website, address, categories and coordinates come from the search surface,
so those are null on a URL-only run.
Can I search several cities in one run? location is one value applied to every term, so run the
Actor once per city β or put each city into the search terms themselves and leave location empty.
Feedback
Something wrong in the data, or a field you need that is not here? Open an issue on the Actor's Store page and we will read it. Bug reports that name the search term, the location and the date of the run are the fastest to act on.