How to Build a Google Maps Scraper (and What to Build Instead)
Google Maps content cannot be scraped for an external listings database. Use Places API for compliant in-app search, or choose data licensed for reuse.
Short answer: You should not build a scraper that copies Google Maps listings, reviews, map tiles, or other Google Maps content into an external dataset. Google Maps Platform’s terms prohibit exporting, extracting, or scraping that content for use outside its services. If your goal is to show places inside an application, use the official Places API and follow its display, attribution, and storage rules. If you need a reusable directory or analytics dataset, use a source whose license permits that specific use.
This is a practical reading of the published platform rules, not legal advice about a particular project or jurisdiction. Confirm the terms that apply to your account and use case before launch. Google Maps Platform Terms of Service state the restriction and give examples including bulk downloads of Places information and copying business names, addresses, or reviews.
1. What people mean by “Google Maps scraper”
The phrase can refer to several different projects, and they do not have the same compliant implementation:
- Copy local business listings into a spreadsheet or database: Google’s terms prohibit copying and saving business names, addresses, or reviews, and prohibit bulk downloads of Places information for use outside the services.
- Download map tiles or satellite imagery: Google’s FAQ says these may not be accessed through mechanisms outside Google Maps Platform, including a script that bulk-downloads tiles.
- Build a place search inside your own application: Use Places API operations for the application’s actual user-facing need, and observe the API’s policies for display, attribution, and retention.
- Build a reusable directory, lead list, or analysis dataset: Choose an independently licensed source whose terms explicitly permit the intended collection, storage, analysis, and redistribution.
A Places API key does not turn the API into permission to create a permanent external directory from Google Maps content. The API is an application integration route, subject to its terms and policies.
2. Patterns to avoid
Do not automate Google Maps pages or services to extract content for external use. The published examples and related guidance cover several common approaches:
- Bulk collecting place names, addresses, reviews, geocodes, or other Places information.
- Prefetching, indexing, storing, resharing, or rehosting Google Maps content outside the services.
- Downloading map tiles or Street View imagery in bulk.
- Copying visible page content into a directory, spreadsheet, or analytics warehouse.
- Using browser automation, intercepted requests, or another access path to accomplish the same external extraction.
Google’s terms section on scraping describes the restriction and examples. Its Maps Platform FAQ separately addresses tile and satellite-image access. This article does not provide endpoint scraping, tile downloading, browser interception, proxy rotation, CAPTCHA bypass, or other evasion instructions.
3. Use Places API for in-app place search
For an application that helps its own users find places, the official route is Places API (New). Choose an operation that matches the user’s task: Nearby Search for a location and optional place types, Text Search for a text query, or Place Details when you already have a place ID and need supported details. Request only the fields the feature needs. A field mask is required for Nearby Search and affects the returned data and billing tier.
The runnable examples below call Nearby Search for restaurants within 500 meters of a sample coordinate. Replace the coordinate, type, and field mask with those required by your application. They request display names and formatted addresses, so the response contains Google Maps content and must be handled under the applicable policy.
Before making a request
- Create or select a Google Cloud project, enable Places API (New), configure billing, and create an API key.
- Restrict the key to the Places API and to the server environment or application that needs it. Do not commit production keys to source control or ship an unrestricted server key to a browser.
- Choose the fields required by the feature. Check the current Place Data Fields page and field-mask guidance for field names and SKU tiers.
- Display Places results according to the Places API policies. If results are shown on a map, Google requires them to appear on a Google Map with the applicable attribution; attribution rules also apply when results are shown without a map.
- Review the storage rules before retaining any response fields. The `place_id` exception is narrow: place IDs are exempt from caching restrictions and can be stored indefinitely. That exception does not apply to other place fields.
cURL
export GOOGLE_MAPS_API_KEY="YOUR_API_KEY"
curl -sS -X POST \
-H "Content-Type: application/json" \
-H "X-Goog-Api-Key: ${GOOGLE_MAPS_API_KEY}" \
-H "X-Goog-FieldMask: places.id,places.displayName,places.formattedAddress" \
-d '{
"includedTypes": ["restaurant"],
"maxResultCount": 10,
"locationRestriction": {
"circle": {
"center": {"latitude": 37.7937, "longitude": -122.3965},
"radius": 500
}
}
}' \
https://places.googleapis.com/v1/places:searchNearby
Python
import os
import requests
api_key = os.environ["GOOGLE_MAPS_API_KEY"]
url = "https://places.googleapis.com/v1/places:searchNearby"
headers = {
"Content-Type": "application/json",
"X-Goog-Api-Key": api_key,
"X-Goog-FieldMask": (
"places.id,places.displayName,places.formattedAddress"
),
}
payload = {
"includedTypes": ["restaurant"],
"maxResultCount": 10,
"locationRestriction": {
"circle": {
"center": {"latitude": 37.7937, "longitude": -122.3965},
"radius": 500,
}
},
}
response = requests.post(url, headers=headers, json=payload, timeout=30)
response.raise_for_status()
data = response.json()
for place in data.get("places", []):
name = place.get("displayName", {}).get("text", "(no name returned)")
address = place.get("formattedAddress", "(no address returned)")
print(f"{name}: {address}")
Node.js
const apiKey = process.env.GOOGLE_MAPS_API_KEY;
if (!apiKey) throw new Error("Set GOOGLE_MAPS_API_KEY first");
const response = await fetch(
"https://places.googleapis.com/v1/places:searchNearby",
{
method: "POST",
headers: {
"Content-Type": "application/json",
"X-Goog-Api-Key": apiKey,
"X-Goog-FieldMask":
"places.id,places.displayName,places.formattedAddress",
},
body: JSON.stringify({
includedTypes: ["restaurant"],
maxResultCount: 10,
locationRestriction: {
circle: {
center: { latitude: 37.7937, longitude: -122.3965 },
radius: 500,
},
},
}),
},
);
if (!response.ok) {
const detail = await response.text();
throw new Error(`Places API returned ${response.status}: ${detail}`);
}
const data = await response.json();
for (const place of data.places ?? []) {
console.log(
`${place.displayName?.text ?? "(no name returned)"}: ` +
`${place.formattedAddress ?? "(no address returned)"}`,
);
}
These examples illustrate one supported request shape; they are not a tested claim about any specific project configuration. See Google’s current Nearby Search documentation for request fields, supported place types, ranking, response behavior, and current regional notes.
Choose the operation and fields around the user task
| Application need | Possible operation | Design note |
|---|---|---|
| Find relevant places around a point | Nearby Search (New) | Set a location restriction and, if useful, included or excluded types. Use a supported ranking preference. |
| Find a place from a phrase such as “vegetarian food near …” | Text Search (New) | Send a specific query and request only required fields. |
| Show details for a selected place | Place Details (New) | Use the place ID and request only the detail fields the screen needs. |
| Help a user enter a place or address | Autocomplete (New) | Design around user input and selection; do not turn predictions into a bulk directory. |
For Nearby Search, `maxResultCount` is bounded by the API’s documented limit. Do not assume a single request yields every place in a city or that repeatedly varying locations to accumulate a directory is permitted. Check the operation documentation for current limits and supported filters.
4. Display, attribution, and storage rules
Places API content comes with conditions that shape the product, not just the HTTP request:
- Map presentation: Places results displayed on a map must be shown on a Google Map with required attribution. If content is displayed without a Google Map, the policy still specifies attribution requirements. Follow the current policy for the exact presentation.
- Attribution: Preserve Google-provided and other required attribution. Do not obscure, remove, or modify it.
- Retention: Do not prefetch, cache, or store Places content beyond the allowed exceptions. The place ID may be stored indefinitely; do not treat names, addresses, phone numbers, ratings, or other fields as covered by that exception.
- Application policies: Google’s Places policies require publicly accessible terms of use and a privacy policy that incorporate the applicable Google terms and privacy policy.
- Regional terms: The governing terms can depend on the billing address and region. EEA billing addresses generally fall under EEA-specific terms, subject to the stated exception for certain pre-July 8, 2025 integrations that remain unmodified.
Read the current Places API policies and attributions and the applicable Maps Platform terms when implementing. Policies and pricing can change.
5. When you need a reusable business dataset
If the deliverable is an exportable business directory, lead list, market-analysis dataset, or database that persists independently of an application, select a source whose license allows that exact purpose. The right source depends on your geography and intended use; this research does not establish that any particular vendor or public dataset grants those rights.
Evaluate candidate data sources on:
- Use rights: Does the license permit collection, internal analysis, commercial use, storage, and redistribution separately?
- Coverage: Does it cover the target countries, regions, business categories, and level of detail?
- Provenance and freshness: How is the information sourced and updated, and how are corrections or removals handled?
- Attribution and privacy: What notices are required, and does the dataset include personal information that creates additional obligations?
- Export and retention: Can you retain records after a subscription ends, join them to other sources, or redistribute derived data?
- Cost and limits: Compare pricing, update frequency, request limits, and permitted users against your actual workload.
Keep provenance and license terms with your ingestion records. A source being publicly viewable does not, by itself, establish that bulk collection or redistribution is allowed.
6. Or skip the browser setup
If your actual task is taking screenshots of your own pages or other pages you are authorized to capture, ScreenshotNeo is a website screenshot API and MCP server. It does not authorize collecting Google Maps listings or bypassing platform rules. Its API can capture a page for visual review; use it only for a permitted screenshot purpose.
One GET request returns an image or PDF. The example captures the Google Maps homepage as a visual page screenshot, not as a method for extracting its content. See the ScreenshotNeo API documentation before using it.
curl -G "https://api.screenshotneo.com/v1/shot" \
-d access_key=YOUR_API_KEY \
--data-urlencode url=https://www.google.com/maps \
-o shot.webp
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={
"access_key": "YOUR_API_KEY",
"url": "https://www.google.com/maps",
},
timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
access_key: "YOUR_API_KEY",
url: "https://www.google.com/maps",
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`ScreenshotNeo returned ${res.status}`);
const image = Buffer.from(await res.arrayBuffer());
await import("node:fs/promises").then(({ writeFile }) =>
writeFile("shot.webp", image),
);
ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot. Bot checks, blank pages, and failed loads are never billed. Its MCP server lets AI agents take screenshots. The Free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.
7. Troubleshooting Places API requests
| Symptom | Likely cause | What to check |
|---|---|---|
| HTTP 400 or a missing-field-mask error | Nearby Search requires a response field mask. | Set `X-Goog-FieldMask` to valid field paths such as `places.displayName`; omit spaces and verify the names against the current field documentation. |
| HTTP 400 for the search body | A malformed body, unsupported type, invalid radius, or incompatible parameter can invalidate the request. | Validate JSON, coordinates, radius, type values, and operation-specific options against Nearby Search documentation. |
| HTTP 403 or permission error | The API may not be enabled, the key may be restricted incorrectly, or project billing/configuration may be incomplete. | Check the Cloud project, enabled API, key restrictions, and billing configuration. |
| HTTP 429 or quota error | Request rate or project quota has been reached. | Review project quotas and usage, reduce unnecessary calls, and implement bounded backoff for transient errors. |
| Empty or unexpectedly small result set | The location, radius, type filter, or search intent may be too narrow; the API does not promise a complete citywide inventory. | Check coordinates and filters, then revise the user-facing search criteria. Do not turn repeated requests into bulk extraction. |
| Fields missing from a returned place | Fields were not requested, values may be absent, or the selected operation may not return them. | Inspect the response and confirm the field is supported for that operation. Handle absent fields in application code. |
| Results appear without expected Google branding or on the wrong map | The UI may not meet attribution or map display requirements. | Follow the current Places attribution policy and display requirements for the specific presentation. |
| A key works locally but fails in production | Production traffic may not match application or server restrictions configured on the key. | Use separate appropriately restricted keys for server and client contexts; inspect project error details without exposing keys in logs. |
8. Performance, reliability, and cost
- Request only needed fields. Field masks reduce response size and can avoid unnecessarily expensive field tiers. Avoid `*` in production; consult the current field-to-SKU table before choosing fields.
- Keep credentials controlled. Restrict API keys to the required APIs and environments, keep secrets out of public repositories, and rotate keys if exposed.
- Handle failures deliberately. Set a client timeout, check HTTP status before parsing, and use bounded exponential backoff with jitter for retryable transient failures. Do not retry invalid requests or permission errors unchanged.
- Respect quotas and current pricing. Costs depend on operation, requested fields, and current billing rules. Check Google’s live pricing and usage pages for your project before estimating spend; do not infer a fixed per-place price from this example.
- Do not build an unauthorized cache. Caching may reduce latency in many systems, but Places content retention is governed by its specific policy. Cache only where the current terms expressly allow it; place IDs are the stated exception.
- Design for partial data. Names, addresses, and other fields may be absent. Render sensible fallbacks and avoid assuming every result has every field.
9. Launch checklist
- Confirm whether the application needs an in-app place feature or an independently reusable dataset.
- Select the supported Places operation and request only fields required for that user task.
- Restrict credentials, configure billing, and review quotas.
- Implement the required map treatment, attribution, terms of use, and privacy policy.
- Review retention field by field; store place IDs only under their specific exception and do not extend it to other fields.
- Check the current terms for the project’s billing region, including EEA applicability where relevant.
- For a reusable dataset, verify that the source license explicitly permits the intended storage, analysis, and redistribution.
FAQ
Can I save Google Maps place IDs?
Google’s Places policy exempts `place_id` from caching restrictions and says it may be stored indefinitely. The exception is specific to place IDs; check the policy before retaining other fields.
Can I use Places API to create a lead list?
An API key does not grant permission to export Places content into an external directory. The platform terms expressly prohibit scraping or bulk downloading content for use outside the services.
Can I use Places API results on a non-Google map?
The Places policy says results displayed on a map must be displayed on a Google Map with the required attribution. Consult the current policy for your exact use case.
Does a public Google Maps page mean its contents are reusable?
No. Public visibility alone does not establish permission for bulk extraction, storage, or redistribution. Use the official API within its policies or a source licensed for your intended use.
Are these terms identical in every region?
Terms can vary. Google identifies separate EEA terms for customers with an EEA billing address, with a stated legacy integration exception. Confirm the terms that govern your project.


