Skip to main content

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.

Updated 12 min readMarkdown
Complete Guide: How to Scrape Google Maps Places in Python Using a Google Maps Scraper

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:

  1. Python 3.11+ installed locally (apify-client 3.x requires it).
  2. The Apify API client for Python (pip install "apify-client~=3.0").
  3. 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.
  • maxItems caps the whole run, not each query.
  • It does not read individual reviews or reviewer profiles.
  • rating, reviewsCount, category and address can 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 searchQueries share 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() accepts max_total_charge_usd, a limit on the total charged amount.
  • Read the bill from the run object. run.charged_event_counts lists the events charged; the measured run shows apify-actor-start: 1 and apify-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. maxItems accepts up to 5,000 places per run; add more targeted queries to reach more places.

Understanding your data

  • id is Google's feature id for the place; it is what the Actor dedupes on.
  • url uses Google's documented Maps URL format (?api=1&query=...&query_place_id=...) and opens the place in Google Maps.
  • rating is 1.0 to 5.0 or null; reviewsCount is an integer or null.
  • category is the main category label in English, because the search is requested with hl=en; the sample rows show "Pizza restaurant" and "Gluten-free restaurant" for Barcelona.
  • latitude and longitude are unrounded as served.
  • searchQuery and scrapedAt are 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. A minRating that drops every place also ends this way.
  • Status SUCCEEDED with zero rows: searchQueries was empty.
  • Fewer rows than maxItems: minRating dropped 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 maxItems at the number you will actually use; it is the cost.
  • Use minRating when you only want rated places; it also removes nulls.
  • Write queries with the city in them and let searchQuery label the rows.
  • Treat rating, reviewsCount, category and address as 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

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

No comments yet. Be the first to comment.

More from this blog