---
title: "Google Maps reviews scraper"
description: "Scrape Google Maps reviews as JSON: reviewer, star rating, date, review text, owner replies, average rating and star distribution. Runs from Python or MCP."
canonical: https://www.zenrows.com/scrape/google-maps-reviews
updated: 2026-10-20
---

# Google Maps reviews scraper

The Zenrows Google Maps reviews scraper returns a place's reviews as JSON: reviewer name, star rating, relative date, review text and the owner's response, with the place's average rating, the review count per star level, its name, category and address. Fetch in Adaptive Stealth Mode opens the place page and Extract returns the fields. Run it from an API call, the CLI or an AI agent.

- Site: google.com/maps
- Returns: Review text, star ratings, dates, owner replies and rating distribution

## Run it: one Google Maps place in, reviews out

```json Input
{
  "url": "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en",
  "mode": "auto",
  "extract": "auto",
  "proxy_country": "us"
}
```

```json Output
{
  "place": {
    "name": "Franklin Barbecue",
    "category": "Barbecue restaurant",
    "address": "900 E 11th St, Austin, TX 78702"
  },
  "rating_summary": { "average_rating": 4.7, "total_reviews": null },
  "rating_distribution": [
    { "stars": 4, "review_count": 690 },
    { "stars": 3, "review_count": 282 },
    { "stars": 2, "review_count": 103 },
    { "stars": 1, "review_count": 199 }
  ],
  "reviews": [
    {
      "reviewer_name": "J.",
      "star_rating": 4,
      "relative_date": "3 months ago",
      "review_text": "Long queue but worth the wait.\nService and atmosphere: …",
      "owner_response_text": null,
      "owner_response_date": null
    },
    {
      "reviewer_name": "D. D.",
      "star_rating": 5,
      "relative_date": "Edited 5 months ago",
      "review_text": "Brother and I came to Austin to eat and Franklin was our first stop. Didn't have time to wait so we pre ordered the minimum five pounds of meat…",
      "owner_response_text": null,
      "owner_response_date": null
    }
  ]
}
```

Request and output of a live call to a Google Maps place on Oct 20, 2026, trimmed for length. A field is returned when the page shows it.

## What data can you scrape from Google Maps reviews?

Reviews come from the place page, so every response pairs the review list with the numbers that put it in context: the average, the count per star level and the place they belong to. These are the fields, with their paths in the JSON.

| Field | What it holds | Example |
|---|---|---|
| `reviews[].reviewer_name` | Display name of the reviewer | `A. S.` |
| `reviews[].star_rating` | Stars given, 1 to 5 | `5` |
| `reviews[].relative_date` | Post date as Maps prints it, with "Edited" when changed | `Edited 5 months ago` |
| `reviews[].review_text` | The review body, line breaks kept | `Long queue but worth the wait…` |
| `reviews[].owner_response_text` | The business owner's reply, when there is one | `null` |
| `reviews[].owner_response_date` | When the owner replied | `null` |
| `rating_summary.average_rating` | The place's overall star rating | `4.7` |
| `rating_summary.total_reviews` | Total review count, when shown | `null` |
| `rating_distribution[].stars` | A star level | `4` |
| `rating_distribution[].review_count` | Number of reviews at that level | `690` |
| `place.name` | The business the reviews belong to | `Franklin Barbecue` |
| `place.category` | Its main category | `Barbecue restaurant` |
| `place.address` | Street address, to tell branches apart | `900 E 11th St, Austin, TX 78702` |
| `place.phone`, `place.website` | Contact details from the same page | `(512) 653-1187` |

## How to scrape Google Maps reviews with Zenrows

1. **Create a free Zenrows account** and copy your API key from the dashboard.
2. **Point at the place, not the search.** Reviews live on a single place page. Search for the business name with its street address, as in `/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX`, and Maps opens that place.
3. **Fix the language with `hl=en`.** Review dates and labels follow the interface language, so one language across locations keeps `relative_date` parseable.
4. **Send it with the default configuration**: Adaptive Stealth Mode to open the page and Extract to return the fields. Keep only the review paths if you do not need the place details.
5. **Schedule it.** Run every location in Batch each week, keep the scrape date with each row, and compare new reviews against the previous run.

```python Python
# pip install requests
import requests

params = {
    "apikey": "YOUR_ZENROWS_API_KEY",
    "url": "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en",
    "mode": "auto",          # Adaptive Stealth Mode
    "extract": "auto",       # structured JSON
    "proxy_country": "us",
}
response = requests.get("https://api.zenrows.com/v1/", params=params, timeout=180)
response.raise_for_status()

parsed = response.json()["parsed"]
print(parsed["place"]["name"], parsed["rating_summary"]["average_rating"])
for review in parsed["reviews"]:
    print(review["star_rating"], review["relative_date"], review["reviewer_name"])
    print("  ", review["review_text"][:120])
```

```javascript Node.js
// Node 18+ (built-in fetch)
const params = new URLSearchParams({
  apikey: "YOUR_ZENROWS_API_KEY",
  url: "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en",
  mode: "auto",
  extract: "auto",
  proxy_country: "us",
});

const response = await fetch(`https://api.zenrows.com/v1/?${params}`, {
  signal: AbortSignal.timeout(180_000),
});
if (!response.ok) throw new Error(`${response.status}: ${await response.text()}`);

const { parsed } = await response.json();
for (const r of parsed.reviews) console.log(r.star_rating, r.relative_date, r.reviewer_name);
```

```bash cURL
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.google.com%2Fmaps%2Fsearch%2FFranklin%2BBarbecue%2B900%2BE%2B11th%2BSt%2BAustin%2BTX%3Fhl%3Den&mode=auto&extract=auto&proxy_country=us"

# Only the reviews and the rating numbers
curl "https://api.zenrows.com/v1/?apikey=YOUR_ZENROWS_API_KEY&url=https%3A%2F%2Fwww.google.com%2Fmaps%2Fsearch%2FFranklin%2BBarbecue%2B900%2BE%2B11th%2BSt%2BAustin%2BTX%3Fhl%3Den&mode=auto&extract=auto&proxy_country=us&extract_fields=reviews%2Crating_summary%2Crating_distribution"
```

## Input

One request reads one place. The URL decides which business it is, so a review monitor for every branch of a chain is a list of name and address searches with the settings below.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `url` | string | required | A Maps search that opens one place: business name plus street address, such as `https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en` |
| `mode` | string | `auto` | Adaptive Stealth Mode: starts with the lightest setup and adds rendering and protected-site handling only when the page needs them |
| `extract` | string | `auto` | Returns `reviews`, `rating_summary`, `rating_distribution`, `place` and `hours` as JSON in `parsed` |
| `extract_fields` | string | all fields | Comma-separated paths to keep, such as `reviews,rating_distribution` |
| `proxy_country` | string | any | Two-letter country code. Use the country of the place for local formats |
| `wait_for` | string | none | A CSS selector to wait for before the page is read |
| `session_id` | integer | none | Keep the same session for up to 10 minutes across related requests |

```json Batch input
{
  "type": "regular",
  "status": "closed",
  "zenrows_params": {
    "mode": "auto",
    "extract": "auto",
    "proxy_country": "us"
  },
  "tasks": [
    { "url": "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en", "external_id": "franklin-barbecue" },
    { "url": "https://www.google.com/maps/search/Mozarts+Coffee+Roasters+Austin+TX?hl=en", "external_id": "mozarts-coffee" }
  ]
}
```

## Output

The response holds `html`, the rendered place page, and `parsed`, the structured record. The review list sits next to the place details and the opening hours, so one request answers "what do people say" and "which branch is this". Trimmed from the live response:

```json Place reviews
{
  "place": {
    "name": "Franklin Barbecue",
    "category": "Barbecue restaurant",
    "address": "900 E 11th St, Austin, TX 78702",
    "phone": "(512) 653-1187",
    "website": "https://franklinbarbecue.com/",
    "plus_code": "77C9+2F Austin, Texas"
  },
  "rating_summary": { "average_rating": 4.7, "total_reviews": null },
  "rating_distribution": [
    { "stars": 5, "review_count": null },
    { "stars": 4, "review_count": 690 },
    { "stars": 3, "review_count": 282 },
    { "stars": 2, "review_count": 103 },
    { "stars": 1, "review_count": 199 }
  ],
  "reviews": [
    {
      "reviewer_name": "J.",
      "star_rating": 4,
      "relative_date": "3 months ago",
      "review_text": "Long queue but worth the wait.\nService and atmosphere: …",
      "owner_response_text": null,
      "owner_response_date": null
    },
    {
      "reviewer_name": "A. S.",
      "star_rating": 5,
      "relative_date": "3 months ago",
      "review_text": "Franklin Barbecue feels like a place where the anticipation is part of the experience. The line outside can be long, but once you get inside and smell the smoke from the pits, it makes sense why people wait…",
      "owner_response_text": null,
      "owner_response_date": null
    }
  ],
  "hours": [
    { "day": "Monday", "hours_range": "Closed" },
    { "day": "Tuesday", "hours_range": "11 AM-3 PM" }
  ]
}
```

A count can come back `null` when the page does not print it, as the five-star row did here. One review per row is the usual shape for analysis: `pd.json_normalize(parsed["reviews"])` gives you that table, and adding `place.name` and the scrape date as columns keeps it joinable.

## Scrape Google Maps reviews from your coding agent

Claude Code, Cursor, Codex and any MCP client can read Google Maps reviews and summarize them from a prompt.

The agent calls Fetch and Extract on the place page, gets the reviews as fields, and does the reading for you: themes, complaints, changes since last month. Connect the Zenrows MCP server once, or install the CLI and let the agent call it in the terminal.

```bash Connect the MCP server (Claude Code)
claude mcp add zenrows -e ZENROWS_API_KEY=YOUR_ZENROWS_API_KEY -- npx -y @zenrows/mcp
```

```json MCP config (Cursor, Claude Desktop, Windsurf)
{
  "mcpServers": {
    "zenrows": {
      "command": "npx",
      "args": ["-y", "@zenrows/mcp"],
      "env": { "ZENROWS_API_KEY": "YOUR_ZENROWS_API_KEY" }
    }
  }
}
```

```bash Or run the CLI
npx -y @zenrows/cli init
zenrows extract "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en" --proxy-country us
```

```text Then ask your agent
Use Zenrows to get the Google Maps reviews of Franklin Barbecue
(900 E 11th St, Austin, TX). Group them by what people praise and
what they complain about, quote one review for each group, and give
me the star distribution.
```

## Google Maps reviews at scale: Batch and Crawl

**Batch** runs every location you track as one job, with the branch ID as `external_id` on each result. Run it on a schedule, store each review with the run date, and new reviews are the ones whose reviewer and text you have not seen before.

**Crawl** is for the step before: building the location list. Start on your own or a competitor's store locator, keep the links that contain `/locations/`, and the crawl returns every branch page with its address. Turn each address into a Maps URL and send the list to Batch.

```bash Batch (cURL)
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "regular",
    "status": "closed",
    "zenrows_params": { "mode": "auto", "extract": "auto", "proxy_country": "us" },
    "tasks": [
      { "url": "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en", "external_id": "franklin-barbecue" },
      { "url": "https://www.google.com/maps/search/Mozarts+Coffee+Roasters+Austin+TX?hl=en", "external_id": "mozarts-coffee" }
    ]
  }'

# Read the results when the job finishes
curl "https://async.api.zenrows.com/v1/jobs/JOB_ID/results?status=successful" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY"
```

```bash Batch (CLI)
# locations.jsonl: one {"url": "...", "external_id": "...", "extract": "auto"} per line
zenrows batch create locations.jsonl --proxy-country us --wait
zenrows batch results <job-id> --out maps-reviews.jsonl
```

```bash Crawl (cURL)
curl -X POST "https://api.zenrows.com/v1/crawls" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://www.your-brand.com/locations/",
    "depth": 2,
    "max_pages": 500,
    "include_patterns": ["/locations/"]
  }'
```

**Scheduled jobs** run the same Batch job on a timetable, on Zenrows' side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can tell your app when a run finishes, and you can pause or resume the schedule at any time, with no cron server to keep alive.

```bash Scheduled job (cURL)
# Runs every day on Zenrows' side; each run keeps its own results
curl -X POST "https://async.api.zenrows.com/v1/jobs" \
  -H "X-API-Key: YOUR_ZENROWS_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
      "type": "scheduled",
      "status": "closed",
      "schedule": {
        "rate": {
          "every": 1,
          "unit": "day"
        }
      },
      "zenrows_params": {
        "mode": "auto",
        "extract": "auto",
        "proxy_country": "us"
      },
      "tasks": [
        {
          "url": "https://www.google.com/maps/search/Franklin+Barbecue+900+E+11th+St+Austin+TX?hl=en",
          "external_id": "google-maps-reviews"
        }
      ]
    }'

# Or at set times: "schedule": { "calendar": { "times_of_day": ["09:00"],
#   "cadence": { "weekly": { "days": ["mon", "thu"] } } }, "timezone": "Europe/Madrid" }
```

## What teams do with Google Maps reviews

| Use case | Data they pull | Who runs it |
|---|---|---|
| Multi-location reputation tracking | Average rating, star distribution and newest reviews per branch, week over week | Operations and CX teams at chains and franchises |
| Sentiment and topic analysis | Review text and stars, one row per review, for an LLM or a classifier | Data, insights and product teams |
| Response monitoring | Which reviews have an owner reply and how long replies take | Customer care and local marketing teams |
| Competitor benchmarking | Ratings and complaints of nearby rivals in the same category | Restaurant groups, clinics and service businesses |
| Due diligence | Rating trend and recurring complaints before buying or investing in a location | Investors and acquisition teams |
| Agency client reporting | Reviews and ratings for every client location in one sheet | Local SEO and reputation agencies |

## Tips for scraping Google Maps reviews

- **Include the street address in the URL.** Chains have dozens of places with the same name; the address picks the branch whose reviews you want.
- **Store the scrape date next to `relative_date`.** "3 months ago" means something only relative to the day you read it. Keep both, and convert to a calendar month in your pipeline.
- **Watch "Edited" in the date.** An edited review keeps its place but changes text or stars, so compare it with the copy you stored instead of counting it as new.
- **Deduplicate on reviewer plus text.** Reviews carry no ID in the response; the reviewer name with the first characters of the text is a stable key between runs.
- **Read the distribution for trend.** A falling count of fours with a rising count of ones tells you more than a change of 0.1 in the average.
- **Keep reviewer names only if you need them.** For sentiment work, drop `reviewer_name` before storage and keep the stars, date and text.

## Frequently asked questions

### Is it legal to scrape Google Maps reviews?

Reviews on Google Maps are public and visible to anyone who opens the place, and reading them for reputation tracking or research is common. They include reviewers' names, which are personal data under laws such as the GDPR and the CCPA, so collect only what you need, and drop names when you only analyze text and ratings. Check Google's terms for your use case and ask your lawyer if you are unsure.

### Is there an official API for Google Maps reviews?

Google has two. The Places API returns up to five reviews per place in a Place Details call. The Google Business Profile API returns all reviews and lets you reply, but only for locations you own or manage. For competitors' reviews, or more than five per place, teams read the place page itself.

### Which reviews does the scraper return?

The reviews shown on the place page when it loads, with reviewer, stars, relative date, text and the owner's reply, plus the average rating and the count per star level. Run the same place on a schedule to build up its review history over time.

### Can I get the owner's responses to reviews?

Yes. Each review has owner_response_text and owner_response_date , filled when the business has replied and null when it has not, so you can measure reply rates per location.

### How do I scrape Google Maps reviews with Python?

Send a GET request to https://api.zenrows.com/v1/ with your API key, the place URL, Adaptive Stealth Mode and Extract turned on, as in the Python example above. response.json()["parsed"]["reviews"] is a list of review dictionaries. The zenrows package on PyPI wraps the same request.

### How do I scrape reviews for many locations?

Make one URL per location, business name plus address, and send the list as a Batch job with your branch ID on each task. One Batch job accepts more than 10,000 URLs, inline or as a CSV file.

### Can AI agents scrape Google Maps reviews with Zenrows?

Yes. Connect the Zenrows MCP server to Claude, Cursor, Codex or any MCP client, or give your agent the Zenrows CLI. Ask for a place's reviews and a summary, and the agent runs Fetch and Extract and reads the JSON.

### Can I scrape Google Maps on a schedule?

Yes. Create a scheduled Batch job with your Google Maps URLs and the same settings as a single request, and Zenrows runs it on its side: every few hours, once a day, at set times on chosen days, or once at a future date. Each run keeps its own results, a webhook can notify your app when a run finishes, and the schedule can be paused or resumed at any time.

### Which integrations work with the Google Maps reviews scraper?

Zenrows works with n8n, Make, Zapier, Clay, Dify, LangChain, LlamaIndex, CrewAI and Mastra, plus the Python, Node.js and Go SDKs and any HTTP client. A weekly review digest can run in n8n or Zapier and post to Slack or a sheet. See the integrations page for the full list.

### What format does the data come in?

JSON by default, with the reviews and place details in parsed and the rendered page in html . You can also ask for the page as Markdown or plain text to hand the reviews to an LLM.

## More Google Maps scrapers

- [Google Maps scraper](https://www.zenrows.com/scrape/google-maps.md)

All scrapers: https://www.zenrows.com/scrape. Start free: https://app.zenrows.com/register. Docs: https://docs.zenrows.com
