Complete Guide: How to Scrape Google Maps Places in Python Using a Google Maps Scraper
Run the Google Maps Places & Ratings Scraper Apify Actor from Python, read the dataset, and handle the cases the code actually handles.

This complete guide to Google Maps Places & Ratings Scraper shows how to run a google maps scraper from Python through the Apify API and get back one structured row per place: name, category, address, star rating, review count and coordinates. It is written by the Actor's builder: the Actor's behaviour below is read from its source and from a measured run.
When analyzing local business markets or conducting competitive intelligence, Google Maps provides a large directory of points of interest. However, manually aggregating locations, star ratings, review counts, and precise GPS coordinates is impractical at scale.
In this Google Maps Places & Ratings Scraper tutorial, we will use Python and the dedicated Apify Actor reventadata/google-maps-scraper to extract structured place records efficiently; proxy handling and retries are built in. Technical prerequisites are minimal: Python 3.11+, one pip package, and an Apify API token. Estimated time to complete: 5 to 10 minutes; the measured run behind the guide finished in 5.3 seconds for five places.
TL;DR
In this tutorial, you'll learn to start the Google Maps Places & Ratings Scraper Apify Actor from Python, wait for the run, check its status, read the dataset and export it. Key steps: install apify-client, set APIFY_API_TOKEN, define searchQueries and maxItems, call the Actor, read run.default_dataset_id. The Actor lives at apify.com/reventadata/google-maps-scraper. Expected outcome: a list of dicts shaped like the sample record further down.
Prerequisites and setup
To follow this tutorial, you will need:
- Python 3.11+ installed locally (apify-client 3.x requires it).
- The Apify API client for Python (
pip install "apify-client~=3.0"). - An Apify account and API token.
You need an Apify account. Apify Console is the web interface where runs, datasets and tokens are managed; the token is on its Settings > API & Integrations page. The platform documentation index is at docs.apify.com.
Install the Apify client library via pip:
pip install "apify-client~=3.0"
Set your API token in your environment:
export APIFY_API_TOKEN="your_apify_token_here"
Understanding Google Maps Places & Ratings Scraper
The Actor is a Python program running in a container on the Apify platform. For each query it builds a desktop Google Maps search URL (hl=en), fetches it through Apify Proxy (the Unblocker group by default), reads the place-data link that the search page preloads, fetches that, and parses the place list into rows. One row is pushed per place; a place id seen under an earlier query is not pushed again.
Blocking is detected by the body of the response, never by the HTTP status code: a Google "sorry" or "unusual traffic" page triggers a retry on a fresh proxy session after a 3 second pause. Retries stop when fewer than 90 seconds of run time remain, so a failing run ends with its real error instead of a timeout. A run that pushes zero rows raises on purpose, so the platform marks it FAILED.
Advantages over alternatives:
- It needs no Google Cloud project or Places API key: it reads the public Google Maps search page.
- It sends plain asynchronous HTTP requests (
httpx) with no headless browser, in a 512 MB container; the measured test run took 5.3 seconds.
Limitations to know before you start:
- Each query reads the first Google Maps results list for that search, not an endless scroll.
maxItemscaps the whole run, not each query.- It does not read individual reviews or reviewer profiles.
rating,reviewsCount,categoryandaddresscan be null.
Note on Data Fidelity: This scraper extracts aggregate place metrics (the overall rating and total review count) and geographic coordinates. It does not scrape individual review text bodies or reviewer profiles.
Step-by-step guide to using Google Maps Places & Ratings Scraper
Step 1: Accessing the Actor
Navigate to the Google Maps Places & Ratings Scraper page on Apify Store. The page lists the input fields, output columns, and current pricing. The page also offers a "Try for free" button for running it in Apify Console. This tutorial drives the Apify Actor from Python so the workflow is automated and reproducible.
Step 2: Configuring input parameters
The Actor takes an array of search queries (e.g. "pizza barcelona") and an optional cap on the total number of places saved in the run (20 if unset). Each query returns the first Google Maps results list for that search, so add more specific queries (e.g. by neighbourhood) to collect more places:
import os
from apify_client import ApifyClient
# 1. Initialize ApifyClient with token from environment
client = ApifyClient(os.environ["APIFY_API_TOKEN"])
# 2. Define the extraction parameters
run_input = {
"searchQueries": ["pizza barcelona"],
"maxItems": 10,
}
Every input field, with the exact names from the input schema:
| Field | Type | Behaviour |
|---|---|---|
searchQueries |
array of strings | Searches, written as you would type them into Google Maps. Empty list: the run logs a warning and ends with no rows. |
maxItems |
integer, 1 to 5000 | Total places saved in the run. Default 20. |
minRating |
number, 0 to 5 | Optional. Places rated below it are skipped; unrated places are skipped too. |
proxyConfiguration |
object | Apify proxy settings. Default: Apify Proxy, group UNBLOCKER. |
Three complete configurations, as inline values you can drop into run_input:
| Use case | searchQueries |
maxItems |
minRating |
|---|---|---|---|
| Quick check | ["pizza barcelona"] |
5 |
unset |
| One city, several districts | ["pizza eixample barcelona", "pizza gracia barcelona", "pizza born barcelona"] |
60 |
unset |
| Only well-rated hotels | ["hotels tokyo"] |
20 |
4.5 |
Best practice: write queries the way a person would type them into Google Maps, including the city, and let searchQuery in the output tell you which query found each row.
Step 3: Advanced configuration
proxyConfiguration accepts the standard Apify proxy object. Unblocker is the measured default; the Actor turns TLS verification off only on that hop, because Unblocker terminates TLS with its own certificate, and verifies on every other proxy group.
Performance against thoroughness is a query question, not a setting: more specific queries cover more places; a larger maxItems on one broad query does not. Cost scales with rows saved, so minRating is the lever that trims the bill before a row exists.
Step 4: Optional enrichment features
There are none. The Actor has no paid add-ons and no enrichment tier; every row carries the same eleven fields at the same price.
Step 5: Running the Actor
Run the Actor on the Apify platform. Execution runs in an isolated container with Apify proxy handling (Unblocker by default) and retries:
# 3. Start the Actor run and wait for it to complete
print("Starting Google Maps extraction...")
run = client.actor("reventadata/google-maps-scraper").call(run_input=run_input)
if run is None or run.status != "SUCCEEDED":
status = run.status if run else "unknown"
raise SystemExit(f"Run did not succeed (status: {status}).")
# 4. Fetch the resulting dataset items
dataset = client.dataset(run.default_dataset_id).list_items().items
print(f"Extraction complete! Retrieved {len(dataset)} places.\n")
call() starts the run and waits for completion before returning the Run object; it returns Run | None, so the code checks for None before reading run.status. The run lifecycle in the Console goes from RUNNING to SUCCEEDED or FAILED; the measured run took 5.336 seconds at 512 MB of memory.
Step 6: Examining the results
Iterate through the returned dataset items to display business names, ratings, review counts, and coordinates:
for item in dataset:
title = item.get("title")
# category, address, rating and reviewsCount can be null
category = item.get("category") or "N/A"
rating = item.get("rating")
reviews_count = item.get("reviewsCount")
address = item.get("address") or "N/A"
lat = item.get("latitude")
lon = item.get("longitude")
print(f"Place: {title} ({category})")
if rating is None:
print(" Rating: not rated yet")
else:
print(f" Rating: {rating} / 5.0 ({reviews_count} reviews)")
print(f" Address: {address}")
print(f" Coordinates: ({lat}, {lon})")
print("-" * 50)
Here is a verbatim record returned by the benchmark run for Barcelona:
{
"id": "0x12a4a2eb343246fb:0x4db9be4af396861f",
"title": "Restaurant Pizzeria Edén",
"url": "https://www.google.com/maps/search/?api=1&query=Restaurant+Pizzeria+Ed%C3%A9n&query_place_id=ChIJ-0YyNOuipBIRH4aW80q-uU0",
"category": "Pizza restaurant",
"address": "Carrer de Provença, 332, Eixample, 08037 Barcelona, Spain",
"rating": 4.8,
"reviewsCount": 5778,
"latitude": 41.397219899999996,
"longitude": 2.1649694,
"searchQuery": "pizza barcelona",
"scrapedAt": "2026-10-22T01:35:03.953011+00:00"
}
Step 7: Downloading and exporting data
In Apify Console, Storage > Datasets > Export offers JSON, CSV, XML, Excel, HTML table, RSS and JSONL, with field selection to trim columns. The same export is available via API with ?format=csv (or json, xlsx, xml) or from Python via get_items_as_bytes(item_format="csv") on the dataset client. Unnamed datasets are deleted after your plan's retention period; name the dataset or download it if you need it longer. JSON keeps nulls as nulls; CSV is the format for a spreadsheet.
Working with the API
Authentication is the API token, passed to ApifyClient once. The three Python blocks above are the whole integration: start and wait, read the dataset, loop over the rows.
Error handling that matches what the code does: KeyError if the token variable is unset, a None return from call(), and a FAILED status when the Actor extracted zero rows and raised. The run log in Console carries the Actor's own messages: "Challenge / captcha detected" for a Google block, "Place data not in the expected shape" for a parse failure, "No searchQueries provided in input." for an empty query list.
Webhooks: on the Actor's Integrations tab in Console you can register a webhook that sends a POST request to your URL when a run event such as success or failure occurs; call() also accepts ad-hoc webhooks for a single run. Scheduling: Apify Schedules run an Actor on a cron expression with optional input overrides, from Console, the REST API or the Python client. Rate limits: the official clients implement retries with exponential backoff for failed or rate-limited requests.
Advanced usage patterns
- Batch queries in one run. Several
searchQueriesshare one start event and one dataset; dedupe across queries is built in. One query per neighbourhood covers a city better than one broad query. - Cap spend per run.
call()acceptsmax_total_charge_usd, a limit on the total charged amount. - Read the bill from the run object.
run.charged_event_countslists the events charged; the measured run showsapify-actor-start: 1andapify-default-dataset-item: 5. - Paging.
list_items()makes one API call;iterate_items()pages through larger datasets for you. - Scheduled runs plus webhooks. Combine a cron schedule with an Apify webhook to deliver fresh place data to your application when a run finishes.
- Integrating with no-code tools. Connect the scraper to Zapier or Make to insert new place records directly into apps such as Google Sheets.
- Monitoring and alerts. Configure run failure notifications in Apify Console to receive email alerts if a scrape fails.
- Scaling.
maxItemsaccepts up to 5,000 places per run; add more targeted queries to reach more places.
Understanding your data
idis Google's feature id for the place; it is what the Actor dedupes on.urluses Google's documented Maps URL format (?api=1&query=...&query_place_id=...) and opens the place in Google Maps.ratingis 1.0 to 5.0 or null;reviewsCountis an integer or null.categoryis the main category label in English, because the search is requested withhl=en; the sample rows show "Pizza restaurant" and "Gluten-free restaurant" for Barcelona.latitudeandlongitudeare unrounded as served.searchQueryandscrapedAtare added by the Actor, not read from Google.
Example analysis: group rows by searchQuery, average rating weighted by reviewsCount, and you have a per-district comparison from one run.
Troubleshooting guide
KeyError: 'APIFY_API_TOKEN': export the variable in the same shell that runs the script.- Status
FAILED, log says "Extracted 0 valid records": the Actor raised because nothing was pushed. Read the lines above it in the log for the cause (block or parse shape). A Google Maps layout change shows only this zero-row error. AminRatingthat drops every place also ends this way. - Status
SUCCEEDEDwith zero rows:searchQuerieswas empty. - Fewer rows than
maxItems:minRatingdropped places, or the queries returned fewer results than the cap. - Slow run: the Actor retries a blocked fetch every 3 seconds on a new proxy session until 90 seconds of run time remain, so a run that is being blocked takes longer before it fails.
Logs are on the run detail page in Console. For platform questions, check Apify's documentation at docs.apify.com, visit the Apify Community Discord, or open an issue on the Actor's Apify Store page.
Best practices
- Keep
maxItemsat the number you will actually use; it is the cost. - Use
minRatingwhen you only want rated places; it also removes nulls. - Write queries with the city in them and let
searchQuerylabel the rows. - Treat
rating,reviewsCount,categoryandaddressas nullable in your own code, as the loop above does. - Do not add your own retry loop around
call(); the Actor retries inside the run on its own clock.
Pricing
For current pricing and details, see the Store page.
Conclusion and next steps
You now have a script that runs a Google Maps scraper on the Apify platform, checks the run status, reads the dataset and knows which fields can be null. Try Google Maps Places & Ratings Scraper yourself and let me know what you build. A follow-up in this series could cover scheduling the same run and pushing the dataset into a spreadsheet.
This is an unofficial tool, not affiliated with or endorsed by Google.
Resources
- Actor page: Google Maps Places & Ratings Scraper
- Apify documentation: docs.apify.com
- Python client reference: ActorClient.call and DatasetClient
- Dataset storage and export formats: Dataset
- Schedules: Schedules
- Webhooks: Webhook integration
- Apify Community Discord: discord.com/invite/apify
About the author: reventadata builds and publishes scrapers on the Apify Store.
About the author
- Hi, I’m the engineer behind ReventaData. I build automated web scrapers, reverse-engineer undocumented APIs, and design data pipelines on Apify that turn complex, unstructured websites into clean, reliable feeds. Here on Hashnode, I share practical tutorials on modern web scraping with Python, bypassing aggressive anti-bot protections (Cloudflare, Akamai, PerimeterX), and data pipeline automation, reverse-engineering hidden endpoints, and automating data extraction. Explore our turnkey scrapers: https://apify.com/reventadata
Comments
Join the discussionNo comments yet. Be the first to comment.




