HELP CENTER

How to Scrape Google Maps Data with Python Using DataForSEO SERP API

Building your own Google Maps scraper looks easy until you run it at scale. Then the real work starts: rotating proxies so you don’t get blocked, a captcha solver for when Google throws a challenge, and a parser you end up rewriting every time the page layout shifts. For a solo builder or a small growth team, that’s a maintenance job nobody signed up for.

DataForSEO SERP API skips that whole stack. You send a query, and we send back structured JSON for the businesses Google shows on Maps. This guide covers how to pull that data with Python, from the first request to a CSV of listings you can actually use.

Why scrape Google Maps?

So why scrape Google Maps in the first place? Because of what’s inside it: point-of-interest (POI) data, meaning the businesses, ratings, and contact details attached to a real physical place. That data turns out to be useful for a lot of everyday work:

  • Local lead lists. Pull every plumber, dentist, or gym in a city, along with names, phone numbers, and websites.
  • Competitor research. See who shows up in the local pack for a query, and how their review counts compare.
  • Directory or dataset building. Fill a niche directory or a Sheets/Notion table without copy-pasting by hand.
  • CRM enrichment. Add addresses, categories, and coordinates to records you already have.
  • No-code and AI workflows. Feed clean listings into n8n, Make, or an LLM prompt instead of scraping raw HTML.

One request to our Google Maps SERP API returns up to 100 listings, or up to 700 if you raise the depth parameter. Each listing already includes the fields you’d otherwise gather by hand: business name, rating and review count, category, full address, phone number, website, the Google place_id and cid, along with the latitude and longitude. That’s the gap between a job you finish in seconds and one that eats an afternoon.

The place_id and cid are worth a closer look. They’re stable identifiers, so you can come back to the same business later to refresh its rating or hours without trying to match on a name that might be written three different ways.

How the Google Maps SERP API works

You can pull Maps results two ways, and it’s worth knowing both before you pick one. There’s a live endpoint that returns results in a single call, which is the quick path for a one-off lookup. There’s also the task-based flow: you post a task with your query, we crawl it, and you come back for the finished result with a separate request. This walkthrough uses the task-based flow, and here’s why it’s the better fit once you’re doing more than a handful of lookups.

The task-based flow decouples asking from collecting. You can post a batch of queries at once, say one per city or category, and we crawl them in parallel while your script moves on instead of holding a connection open for every call. You don’t have to sit and poll, either: set a pingback or postback URL and we’ll ping your server the moment a task finishes. For a lead list or a directory that refreshes on a timer, that hands-off style is exactly what you want.

There are three moving parts:

  • task_post. You send the keyword, location, and language, and we queue the job and return a task id.
  • tasks_ready or pingback. You either poll for finished tasks or have us ping your server the moment one is done.
  • task_get. You fetch the structured JSON by task id.

Authentication is HTTP Basic, using the login and password you’ll find in your dashboard after signing up. Location matters more here than on most endpoints. Maps results are local by nature, so you have to specify either location_code or location_name, or location_coordinate. Want the live-endpoint version for standard search first? We walk through that in our guide to scraping Google Search results with Python. Otherwise, on to the code.

Build a Google Maps scraper in Python: full walkthrough.

The first step is to post a task. You send an array holding one query object with the keyword, location, and language, then read the task id back from the response. For example, we’ll look up coffee shops in a single city and take the default 100 results.

import requests

# HTTP Basic auth — find your login and password in the dashboard after signing up.
# requests turns this tuple into a base64-encoded Authorization header for you.
LOGIN, PASSWORD = "your_login", "your_password"

# One query object per task. location_code 2840 = United States;
# raise depth (up to 700) only if you need more than the default 100 listings.
task = [{
    "keyword": "coffee shops in austin",
    "location_code": 2840,
    "language_code": "en",
    "depth": 100,
}]

resp = requests.post(
    "https://api.dataforseo.com/v3/serp/google/maps/task_post",
    auth=(LOGIN, PASSWORD),
    json=task,
    timeout=60,
)
task_id = resp.json()["tasks"][0]["id"]
print("Task posted:", task_id)

See the docs.

With the task-based flow, the data isn’t ready the moment you post the task. The next snippet fetches the task by its id and checks the status code. A 20000 means the result is ready. A queue code means wait a few seconds and try again.

import time

get_url = "https://api.dataforseo.com/v3/serp/google/maps/task_get/advanced/" + task_id

# status_code 20000 = result ready; 40602 = "task in queue, not finished yet".
result = None
for _ in range(10):
    result = requests.get(get_url, auth=(LOGIN, PASSWORD), timeout=60).json()
    if result["tasks"][0]["status_code"] == 20000:
        break
    time.sleep(10)  # wait for the crawler rather than hammering the endpoint

items = result["tasks"][0]["result"][0]["items"]
print(f"Got {len(items)} listings")

See the docs.

Last step: turn the JSON into rows. Each business sits in the items array, so we grab the fields that matter for a lead list or directory and write them to a CSV. From there, you can open it in Sheets or pass it to a no-code tool.

import csv

rows = []
for item in items:
    # The feed can include non-listing blocks; keep only actual Maps businesses.
    if item.get("type") != "maps_search":
        continue
    rating = item.get("rating") or {}  # rating is a nested object, so guard against None
    rows.append({
        "name": item.get("title"),
        "rating": rating.get("value"),
        "reviews": rating.get("votes_count"),
        "category": item.get("category"),
        "address": item.get("address"),
        "phone": item.get("phone"),
        "place_id": item.get("place_id"),
        "lat": item.get("latitude"),
        "lng": item.get("longitude"),
    })

with open("maps_results.csv", "w", newline="") as f:
    writer = csv.DictWriter(f, fieldnames=rows[0].keys())
    writer.writeheader()
    writer.writerows(rows)

Run those three snippets in order, and you’ve got a reusable Google Maps scraper in Python. You’re not maintaining a proxy pool, a captcha service, or a parser that breaks every time Google reshuffles the page. To cover more than one city, wrap the query object in a loop and post one task per location. Collect the IDs, fetch them once they’re ready, and run the same parsing step over each result.

Common mistakes to avoid

A handful of things trip people up on a first integration, and each one is quick to fix once you know it’s there.

  1. Polling too hard. Hitting task_get in a tight loop just burns through requests. Use tasks_ready to see what’s finished, or set a pingback_url and let us tell you when the task is done.
  2. Ignoring depth and cost. You’re billed for setting each task, and going past 100 results or bumping priority costs more. Set the depth to the number of results you need. The full breakdown is on the Google Maps SERP API pricing page.

Wrapping up

Scraping Google Maps the hard way means running infrastructure you never wanted in the first place. The task-based flow in our SERP API reduces it to three short Python steps: post a task, fetch the result, and export the listings
You pay per request, so a one-off project stays a one-off rather than becoming another monthly subscription. For a small team, that trade is easy: structured point-of-interest data when you need it, and no scraping stack to babysit. Sign up, grab your credentials, and run the walkthrough above on a query of your own.

Try for free

Embed DataForSeo widget on your website


Embed code:
Preview: