To scrape Google Maps, send a search term and a location to a scraper that reads Google Maps' search results, and save each place it returns: name, address, rating, review count, phone, website, opening hours and coordinates. The quickest way is a ready-made scraper you run in the cloud, from a web form or with one API call, so you don't have to maintain a headless browser yourself.
This guide shows both: the no-code way and the API way (curl, Python, Node.js and AI agents over MCP). Everything below comes from a real run we made on 22 October 2026: the exact input, the output, the log and what it cost.
What data you can get from Google Maps
A Google Maps search returns place cards. A scraper can save everything the card and the place panel show. These are the fields the Google Maps Scraper on Scrape.ly writes:
| Field | What it holds |
|---|---|
title | The place's name |
categoryName, categories | The primary category and the full category list |
address, street, city, postalCode, countryCode | The address, whole and split into parts |
location | Latitude and longitude |
phone, phoneUnformatted | The business phone, as shown and digits only |
website | The website linked from the card |
totalScore, reviewsCount | Star rating and number of reviews |
openingHours | Opening hours per day |
placeId, cid, fid, kgmid | Google's identifiers for the place |
url | A Google Maps link to the place |
description, subTitle | The short description, when Google shows one |
reviewsTags | Review topics and how many reviews mention each |
ownerUpdates | Recent posts from the business owner |
rank, isAdvertisement | Position in the search and whether it was an ad |
Some fields only appear when the place has them: the scraper does not invent a phone number, a review count or a closed flag. Review texts and email addresses are not part of a place card, so they need different tools (see the FAQ).
How to scrape Google Maps without code
- Open the Google Maps Scraper in the Scrape.ly Store.
- Pick a ready-made input. We used the preset Restaurants in Konstanz, 10 places. "Try this input" opens it in the console.
- Change the search term and the location to yours. This is the exact input of our run:
{
"categoryFilterWords": [],
"language": "en",
"locationQuery": "Konstanz",
"maxCrawledPlacesPerSearch": 10,
"placeMinimumStars": "",
"searchMatching": "all",
"searchStringsArray": ["restaurant"],
"skipClosedPlaces": false,
"website": "allPlaces"
}- Start the run. Places appear in the dataset as each search finishes.
- Export the dataset as JSON, CSV or Excel.
The fields that matter most:
searchStringsArray: one or more search terms, typed as you would type them into Google Maps. Several terms run in parallel.locationQuery: the city or area the map search is centered on, for example "New York, USA".maxCrawledPlacesPerSearch: how many places to keep per search term (default 20).placeMinimumStars,website,skipClosedPlaces,categoryFilterWords: filters for a minimum rating, places with or without a website, places Google marks as closed, and words the category must contain.language: the Google Maps language code, which sets the language of categories and descriptions.
How to scrape Google Maps with the API
Create an API token in the dashboard under Settings → API & Integrations, then export it as SCRAPELY_TOKEN.
The run above took under 6 seconds, so the synchronous endpoint works: it starts the run, waits up to 45 seconds and returns the places as a JSON array. Add ?format=csv for CSV.
curl -X POST "https://api.scrape.ly/v2/actors/cornerledger~google-maps-scraper/run-sync-get-dataset-items" \
-H "Authorization: Bearer $SCRAPELY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchStringsArray": ["restaurant"], "locationQuery": "Konstanz", "maxCrawledPlacesPerSearch": 10}'If you ask for many places and a run takes longer than 45 seconds, the call answers 408 while the run keeps going. Don't call it again (that starts a second run); wait for the run instead, or use a client, which waits as long as it takes.
Python (pip install scrapely-client-python):
import os
from scrapely_client import ScrapelyClient
client = ScrapelyClient(os.environ["SCRAPELY_TOKEN"], api_url="https://api.scrape.ly")
run = client.actor("cornerledger/google-maps-scraper").call(
run_input={"searchStringsArray": ["restaurant"], "locationQuery": "Konstanz", "maxCrawledPlacesPerSearch": 10}
)
print(run.status) # SUCCEEDED, FAILED, ABORTED or TIMED-OUT
for item in client.dataset(run.default_dataset_id).iterate_items():
print(item["title"], item.get("totalScore"), item.get("phone"))Node.js (npm install scrapely-client):
import { ScrapelyClient } from 'scrapely-client';
const client = new ScrapelyClient({ token: process.env.SCRAPELY_TOKEN, baseUrl: 'https://api.scrape.ly' });
const run = await client.actor('cornerledger/google-maps-scraper').call({
searchStringsArray: ['restaurant'],
locationQuery: 'Konstanz',
maxCrawledPlacesPerSearch: 10,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items.map((place) => [place.title, place.totalScore, place.website]));To let an AI agent do it, add the Scrape.ly MCP server. In Cursor, put this in ~/.cursor/mcp.json:
{
"mcpServers": {
"scrapely": {
"url": "https://api.scrape.ly/mcp",
"headers": { "Authorization": "Bearer YOUR_TOKEN" }
}
}
}In Claude Code, run claude mcp add --transport http scrapely https://api.scrape.ly/mcp and sign in with /mcp. Then ask in plain words: "Use scrapely to run cornerledger/google-maps-scraper for restaurants in Konstanz, 10 places, and give me a CSV of name, rating and phone." The agent finds the Actor, reads its input schema, runs it and reads the results. See the MCP docs, the Python client and the Actors API for the details.
A real run: Restaurants in Konstanz, 10 places
We ran the "Restaurants in Konstanz, 10 places" preset on 22 October 2026. It returned 10 places in 5.7 seconds and was charged $0.0369. The first five rows:
| Place | Category | Rating | Reviews | Street | Phone | Website |
|---|---|---|---|---|---|---|
| The Dom | Italian restaurant | 4.7 | 1,136 | Brückengasse 1 | +49 7531 5847369 | thedom-konstanz.de |
| Constanzer Wirtshaus | German restaurant | 4.4 | 7,521 | Spanierstraße 3 | +49 7531 3630130 | constanzer-wirtshaus.de |
| Chu Minh Restaurant | Asian restaurant | 4.7 | 249 | Frieda-Sigrist-Weg 1 | +49 7531 9325998 | chuminh-restaurant.de |
| Restaurant Zur Wendelgard | German restaurant | 4.7 | 848 | Inselgasse 5 | +49 7531 917488 | zur-wendelgard.de |
| Brasserie Chez Léon | Brasserie | 4.6 | 1,754 | Zollernstraße 1 | +49 7531 282728 | chezleon.de |
These are businesses, so their public listing data is shown as Google shows it. The full record for The Dom also carried its opening hours, kitchen hours, coordinates, place IDs, a reservation link, an accessibility note, review topics ("potato wedges" in 14 reviews) and a recent owner post.
The run's log, without the platform's start-up lines:
2026-10-22T15:22:50.496Z [Scrapely] Container started (setup 0.9 s)
2026-10-22T15:22:51.765Z [scrapely.actor] INFO [Scrapely] Initializing API storage for run a323f602472f41838
2026-10-22T15:22:55.025Z [scrapely.actor] INFO Saved 10 places in 3.2s (11 requests, 0 hedged, 0.93 MB); start-up: python 1.13s, SDK import 0.00s, SDK init 0.27s
2026-10-22T15:22:55.374Z [Scrapely] Container state: status=exited exitCode=0 oomKilled=falseEleven requests and under a megabyte of traffic for ten places: the scraper reads Google Maps' own search data with plain requests instead of rendering the map in a browser.
We also ran the Italian restaurants in Konstanz preset, which adds "categoryFilterWords": ["italian"]. It also returned 10 places, in 9.5 seconds ("Saved 10 places in 6.9s (12 requests, 1 hedged, 1.51 MB)"), because the scraper reads further down the results to replace the places the filter drops. Only two places appeared in both runs. Two of the filtered ten have a primary category without "Italian" (a brasserie and a plain "Restaurant"), so the filter evidently matched another entry in their category list. Check categories if you need strict matches.
What it costs
The Google Maps Scraper charges $0.0036 per place saved to the dataset, which is $3.60 per 1,000 places. On top of that, a run pays for the platform resources it uses.
For our run: 10 places × $0.0036 = $0.036 in place fees. The run was charged $0.0369 in total, because it also used $0.00038 of compute and $0.0005 of proxy traffic. A search for 1,000 places would cost $3.60 in place fees plus usage of the same kind.
Filters don't add fees: places the filter drops are not saved and not charged.
Limits and common problems
- One search reads up to about 200 places. When filters drop places, the scraper reads further down the results to fill your limit, up to about 200 places per search. To cover a whole city, split it into several searches (by district or by more specific terms).
- The location centers the search; it is not a boundary. Results come from around
locationQuery, so a few places can sit outside the city you named. Filter oncityorpostalCodeif that matters. - Only what the card shows. No review texts, no emails, no owner names. Fields that Google doesn't show for a place are missing, not guessed.
- Language changes text, not places.
languagesets the language of categories and descriptions. Keep it fixed across runs if you compare them. - Closed places.
skipClosedPlacesdrops a place only when its record says it is permanently or temporarily closed.
Is it legal to scrape Google Maps?
Place cards are public, and much of their content is factual business information. That doesn't settle the question. Google's Maps Additional Terms of Service say users may not "mass download or create bulk feeds of the content" or use Google Maps to create a business listings database "for use in a service that is a substitute for" Google Maps. Whether and how those terms bind you depends on where you are and how you use the data.
Privacy law applies too. A sole trader's phone number or address can count as personal data under the GDPR or the CCPA, even on a business listing. Collect only what you need, keep it secure, and have a lawful basis for using it. This is not legal advice: if the data feeds a product or an outreach campaign, ask a lawyer about your specific case.
Alternatives
- Google's Places API. The official Places API (New) offers text search, place details, photos and reviews under Google's terms, with an API key and billing set up. Use it if you display places inside your own app and want a contract with Google.
- Open source. gosom/google-maps-scraper ranks first on Google for this topic. It is free to run on your own machine; you handle the servers, proxies and updates.
- Build it yourself. Bright Data's Selenium guide walks through eleven steps, from the cookie dialog to
aria-labelselectors. It's a good way to learn how the page works, but Google's class names change between deployments, so expect maintenance. - Other hosted scrapers. Outscraper and Apify also sell Google Maps scraping. Compare the price per place, the fields and how they bill platform usage.
FAQ
Can you scrape Google Maps for free?
Yes, if you run an open-source scraper on your own computer and accept the setup and blocking work. A hosted scraper costs money: on Scrape.ly it's $0.0036 per place, so our 10-place run cost under 4 cents.
How many results can you scrape from one Google Maps search?
Up to about 200 places per search term. For more, run several searches with narrower terms or locations; the scraper runs several search terms in parallel.
Can I scrape emails from Google Maps?
Google Maps place cards don't contain emails. The Google Maps Email Extractor finds businesses on Google Maps and then reads their websites for emails, phone numbers and social links.
Can you scrape Google Maps reviews?
Yes, with a separate scraper: the Google Maps Reviews Scraper returns review text, stars, date and owner reply for a place. Reviews are written by private people, so treat reviewer names as personal data.
Does it work in any country and language?
It searches around any location you name, such as "Konstanz" in our run or "New York, USA", and language takes a Google Maps language code like en or de.




