How to Scrape Google Maps Data with Python and an API

By Serpent API Team · · 10 min read

This tutorial exports ranked Google Maps business records to CSV with Python. It uses the dedicated Maps Quick contract—not the older web-search local pack—and preserves completion status so a missing field is never silently treated as confirmed missing data.

The script was executed against the local Serpent gateway on July 2, 2026. The test query barber in Akron, Ohio returned 20 places: 19 complete and one core_only. Four complete records had no website. Results vary by query, location, and time, and the Maps endpoints are still pre-launch rather than verified production services.

Choose the right Maps data source

“Google Maps API” can mean several products. Google Maps Platform is the official choice for applications built around Google’s maps, places, routing, and display ecosystem. A business-data API is aimed at discovery workflows: search a category and location, receive ranked business records, then fetch a place or its public reviews by identifier.

Before integrating, write down the fields and unit you need. For a discovery export, the useful baseline is rank, name, category, address, coordinates, phone, website, rating, review count, hours, and stable identifiers. If you need one profile, use a place-detail endpoint. If you need review text and owner responses, use a paginated reviews endpoint. Do not assume one “Maps” call includes all three jobs.

Understand the normalized response

Maps Quick returns up to 20 ranked places. A complete record includes richer business fields; a bounded request may instead preserve the ranked core record and mark it core_only. The top-level meta.partial flag tells you whether any record is partial.

{
  "success": true,
  "query": "barber",
  "places": [
    {
      "rank": 1,
      "name": "Example Barber",
      "categories": ["Barber shop"],
      "address": {"formatted": "Akron, OH"},
      "coordinates": {"latitude": 41.0814, "longitude": -81.5190},
      "phone": "+1 330 ...",
      "website": "https://example.com",
      "rating": 4.8,
      "review_count": 214,
      "place_id": "...",
      "data_id": "...",
      "detail_status": "complete"
    }
  ],
  "counts": {
    "returned": 20,
    "fully_enriched": 19,
    "core_only": 1
  },
  "meta": {"partial": true}
}

Use rank for returned order and stable IDs for deduplication. Do not deduplicate only by name: chains and unrelated businesses can share one.

Scrape Google Maps data with tested Python

Install Requests, set a test key in the environment, and keep the default local URL while validating. The code intentionally exports partial rows but leaves detail-derived values blank and retains detail_status.

import csv
import os
import requests

API_KEY = os.environ["SERPENT_API_KEY"]
BASE_URL = os.getenv("SERPENT_BASE_URL", "http://localhost:3001")

FIELDS = [
    "rank", "name", "category", "address", "latitude", "longitude",
    "phone", "website", "rating", "review_count",
    "place_id", "data_id", "detail_status",
]

def fetch_places(query, location, country="us"):
    response = requests.get(
        f"{BASE_URL}/api/maps/search/quick",
        headers={"X-API-Key": API_KEY},
        params={
            "q": query,
            "location": location,
            "country": country,
            "language": "en",
        },
        timeout=90,
    )
    response.raise_for_status()
    payload = response.json()
    if not payload.get("success"):
        raise RuntimeError(payload)
    return payload["places"], payload.get("meta", {})

def flatten_place(place):
    address = place.get("address") or {}
    coordinates = place.get("coordinates") or {}
    categories = place.get("categories") or []
    return {
        "rank": place.get("rank", ""),
        "name": place.get("name", ""),
        "category": ", ".join(categories),
        "address": address.get("formatted", ""),
        "latitude": coordinates.get("latitude", ""),
        "longitude": coordinates.get("longitude", ""),
        **{field: place.get(field, "") for field in FIELDS[6:]},
    }

def export_csv(places, filename):
    with open(filename, "w", newline="", encoding="utf-8") as handle:
        writer = csv.DictWriter(handle, fieldnames=FIELDS)
        writer.writeheader()
        writer.writerows(flatten_place(place) for place in places)

if __name__ == "__main__":
    places, meta = fetch_places("barber", "Akron, OH")
    export_csv(places, "akron_barbers.csv")
    complete = sum(p.get("detail_status") == "complete" for p in places)
    print({
        "returned": len(places),
        "complete": complete,
        "core_only": len(places) - complete,
        "partial": bool(meta.get("partial")),
    })

Run it with:

python -m pip install requests
export SERPENT_API_KEY="your_test_key"
python maps_to_csv.py

Our dated run printed 20 returned, 19 complete, one core-only, and partial: true. The CSV contained 20 data rows plus the header.

Handle partial records correctly

A partial response is a successful response with less detail, not a failed search. Keep it if rank and identity are useful, but do not make detail-dependent claims from it. In particular:

The bounded Deep endpoint follows the same rule at a larger requested result count. A local test returned 97 places in 88.2 seconds: 85 complete and 12 core-only. That is one measured run, not a latency guarantee.

Scale a Google Maps data export without corrupting it

For multiple cities, create an explicit list of query/location pairs and save each response before merging. Deduplicate by place_id or data_id; use normalized name plus address only as a fallback. Keep location context because the same place can rank differently across searches.

Maps Quick accepts either a human-readable location or a complete lat/lng pair, not both. Coordinates help with controlled local comparisons, but they do not guarantee complete coverage of every business in a region. Overlapping searches create duplicates, and sparse categories can return fewer than the endpoint maximum.

For rank-grid work, read the separate geo-grid local rank tracking guide. For prospecting, the no-website lead workflow shows how completion status changes the filter. The Google Maps API service page is the canonical endpoint and pricing reference.

Pricing and limits

Maps Quick costs $15/$7.50/$3.75 per 1,000 calls at Default/Growth/Scale. New accounts get 10 free lifetime calls shared across SERP search and Maps Quick. Deep costs $75/$37.50/$18.75; Place costs $1.50/$0.75/$0.375; Reviews costs $3/$1.50/$0.75 per 1,000 calls.

These are per-call rates. A full 20-place Quick response has a best-case effective cost of $0.75 per 1,000 returned places at Default. A five-place response costs $3 per 1,000 returned places. Returned counts and detail completeness are not guaranteed.

Legal and data-use limits

Review the official Google Maps terms, Google Maps Platform terms where applicable, privacy law, and the rules for storage, display, outreach, and redistribution in your jurisdiction. Publicly visible data is not automatically unrestricted data. This tutorial is technical information, not legal advice.

Read the complete Maps contract

Review all four endpoint parameters, normalized fields, partial-response behavior, exact tier pricing, and pre-launch status before integrating.

Open the Google Maps API Guide

Documentation · Local Playground · Pricing

FAQ

Can Python export Google Maps business data to CSV?

Yes. Request a Maps business-data endpoint with Python, validate its JSON, and write selected normalized fields with csv.DictWriter. The example above was executed against the local Maps Quick endpoint on July 2, 2026.

Which fields should I export?

Start with rank, name, categories, formatted address, latitude, longitude, phone, website, rating, review count, place ID, data ID, and detail status. Preserve detail status so missing values on partial records are not misclassified.

How many places does the example return?

Maps Quick returns up to 20 places. The dated Akron barber query returned 20: 19 complete and one core-only. Result counts vary by query, location, and time.

How much does the Maps Quick API cost?

Maps Quick costs $15, $7.50, or $3.75 per 1,000 calls at Default, Growth, or Scale. New accounts get 10 free lifetime calls shared across SERP search and Maps Quick. This is per-call pricing, not a guaranteed per-place rate.

Is collecting Maps data legally risk-free?

No. Review Google Maps terms, privacy obligations, storage and display restrictions, and your intended use. This guide is not legal advice.