A Google Maps Review Scraper for a Place Shortlist
Fetch recent review pages with Python, keep each request tied to a place, and stop at a clear limit.
The shortlist already has names and addresses. The open question is whether recent reviews mention long waits.
For that, I use the GMapsScraper.AI reviews scraper. The Python script below fetches review pages for one place, saves the full responses, and tells me whether there's another page.
Which branch is this?
The request takes either fid or url. Use the Fid from a business search, or that business's Google Maps place URL. A business name alone won't work here. For chains, compare the full address with your shortlist before copying the reference.
Place Id looks like the obvious field to copy, but the reviews call wants Fid as fid or Google Maps URL as url. Place Id is a different key in the search response, so don't pass it as fid.
Without either reference, search first with POST /api/v1/search. It only needs q, but set ll, hl, and gl for location, language, and country; without ll, the search defaults to a US location. Compare the returned name and address with the shortlist row.
The GMapsScraper.AI API reference covers both calls. Once the address matches, copy that row's Fid or URL into the shortlist.
Review order
The place reference stays the same. Add page and sort_by:
| Value | Order |
|---|---|
1 |
Relevant |
2 |
Newest |
3 |
Highest rating |
4 |
Lowest rating |
I use 2 for this question. If you sort by highest or lowest rating instead, write that in your notes, so nobody reads a lowest-first page as typical.
Newest first isn't a date filter. There's no start or end date in this request. If you need a cutoff, such as the last few months, check the dates in the saved reviews before choosing which ones to include.
The response includes reviews, next, total, params, and place. The boolean next signals another page. The script's --max-pages cap limits each run, and I keep it at one until I've opened the JSON. Anything you conclude from that file covers only the reviews on that page.
Save the raw pages
Create an API key under Dashboard > Settings > Create API Key and set it in the shell you'll use:
export GMAPS_API_KEY='YOUR_API_KEY'
Save this complete block as fetch_reviews.py. It needs only Python's standard library.
import argparse
import json
import os
from pathlib import Path
from urllib.request import Request, urlopen
ENDPOINT = "https://gmapsscraper.ai/api/v1/reviews"
def positive_int(value):
number = int(value)
if number < 1:
raise argparse.ArgumentTypeError("Use a positive integer")
return number
def fetch(key, body):
request = Request(
ENDPOINT,
data=json.dumps(body).encode("utf-8"),
headers={
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
},
method="POST",
)
with urlopen(request, timeout=60) as response:
return json.load(response)
def main():
parser = argparse.ArgumentParser()
target = parser.add_mutually_exclusive_group(required=True)
target.add_argument("--fid")
target.add_argument("--url")
parser.add_argument("--page", type=positive_int, default=1)
parser.add_argument("--max-pages", type=positive_int, default=1)
parser.add_argument("--sort-by", type=int, choices=(1, 2, 3, 4),
default=2)
parser.add_argument("--out", required=True)
args = parser.parse_args()
key = os.environ.get("GMAPS_API_KEY", "").strip()
if not key or key == "YOUR_API_KEY":
parser.error("Set GMAPS_API_KEY to your real API key")
reference = {"fid": args.fid} if args.fid else {"url": args.url}
if not next(iter(reference.values())).strip():
parser.error("The place reference cannot be blank")
folder = Path(args.out)
folder.mkdir(parents=True, exist_ok=False)
for page in range(args.page, args.page + args.max_pages):
payload = fetch(key, {
**reference, "page": page, "sort_by": args.sort_by,
})
path = folder / f"page-{page}.json"
path.write_text(
json.dumps(payload, ensure_ascii=False, indent=2),
encoding="utf-8",
)
if not isinstance(payload.get("next"), bool):
raise ValueError(f"Inspect next in {path}")
if "reviews" not in payload:
raise ValueError(f"Missing reviews in {path}")
print(f"Saved {path}; next={payload['next']}")
if not payload["next"]:
break
if __name__ == "__main__":
main()
Here's a first call. Replace the placeholder with the place URL from the shortlist row:
python3 fetch_reviews.py \
--url 'YOUR_GOOGLE_MAPS_PLACE_URL' \
--sort-by 2 --max-pages 1 --out review-check-01
You can pass --fid 'YOUR_FID' instead of --url. The command refuses both together. If the saved page has next: true, you can continue with --page 2 and a new output folder. --max-pages counts pages from that starting point.
Read page one against the question
Open page-1.json and compare place with the shortlist address and params with the sort you asked for, before reading any review.
Inspect one saved review before choosing the text, date, and rating fields for your parser. The parser reads the saved file, so changing it costs no requests. Print the record keys locally:
import json
from pathlib import Path
saved = Path("review-check-01/page-1.json")
payload = json.loads(saved.read_text(encoding="utf-8"))
reviews = payload["reviews"]
print(type(reviews).__name__)
if isinstance(reviews, list) and reviews:
first = reviews[0]
print(list(first.keys()) if isinstance(first, dict) else first)
My notes for each place stay short: the question, the sort order, the pages I read, and the source page for each point. If I set a date cutoff, it goes in the note too. A teammate can then see what was read without fetching it again.
To call waiting time a common complaint, you need a count. Decide first whether comments like "slow service" belong under waiting time, then count mentions against the number of reviews you read.
When the run stops
Each response is written to disk before the loop looks at next. If a request fails, the script stops there with no retry, and pages saved before the failure stay in the folder. Open the last one before deciding which page to request next. Every run needs a new folder; putting the place in its name keeps pages from two branches apart.
If the last page says next: true, the cap stopped the run while more pages were available. If it says next: false, there are no further pages for that place and sort.
API calls draw on request quota, which is separate from web app credits, and each key is rate limited. Fetching the same place and page again spends another request, so I parse from the saved files.
Read with an MCP client if that's where you work
The GMapsScraper.AI MCP guide covers scrape_google_maps_reviews. It takes fid or url, page, and sort_by, with the same key, quota, rate limits, and response shapes as REST.
Give the client the fid or URL and a short instruction: fetch page one with sort_by: 2, report next, and stop. Then ask the client to list mentions of waiting time, quote each one, and state how many reviews it read. Check each quote against the full review before sharing the summary.
If page one doesn't settle the waiting-time question, fetch page two: --page 2 with a new folder, or ask the client for it.
Reviewer names, and sometimes the review text itself, can identify a person. Keep the saved JSON inside the team, quote only the lines the summary needs, and leave names out of anything that goes further. Check Google's terms and the privacy rules you work under before keeping those files past the task.
Jim Miller, Growth at GMapsScraper.AI
Top comments (0)