Google Maps Scraper - Business Leads & Emails
Eimantas V · @amethyst
Scrape Google Maps places by keyword and city: name, address, phone, website, rating, reviews, hours and coordinates, plus emails and socials from websites.

About
Google Maps Scraper extracts business listings from Google Maps: name, address, phone, website, rating, review count, opening hours, categories, coordinates and IDs, plus optional emails and social profiles from each business's website. Search by keyword and location, or paste Google Maps URLs and Place IDs. No login, no API key, no browser.
{
"title": "Italala Caffè",
"categoryName": "Coffee shop",
"address": "Vokiečių g. 1, Vilnius, 01130 Vilniaus m. sav.",
"phoneUnformatted": "063033363",
"website": "https://www.italala.lt/",
"totalScore": 4.7,
"reviewsCount": 1519,
"location": { "lat": 54.6785822, "lng": 25.2859445 },
"openingHours": [{ "day": "Monday", "hours": "8 am–8 pm" }, { "day": "Tuesday", "hours": "8 am–8 pm" }],
"placeId": "ChIJ6-1bDxWU3UYRue3sXBtJaiQ",
"emails": ["[email protected]"],
"instagrams": ["https://www.instagram.com/italalacaffe"]
}
Measured: 3,000 Manhattan restaurants in 2 min 7 s; 300 Chicago dentists with place details and website contacts in 60 s.
What does Google Maps Scraper do?
It searches Google Maps the way you would, "dentist" in "Austin, Texas", and returns every place as one row. A large location is split into map cells automatically, so a city returns thousands of places instead of the ~120 that one Google Maps search shows. Field and input names are the same as in compass/crawler-google-places, so existing inputs and integrations keep working.
- Search a term in a city, region, country, postal code or a polygon you draw. Or paste Google Maps place links, search links, share links, Place IDs and CIDs.
- Lead data in every row: phone in local and international format, website, full address split into street, city, postal code and state, rating, review count, categories, hours for the whole week, coordinates, Place ID and CID.
- Optional add-ons: emails, phones and social profiles (Facebook, Instagram, LinkedIn, X, YouTube, TikTok, Pinterest) from each business's website, and place details with the 1-5 star breakdown, review topics and the top reviews.
- Filters: category, minimum rating, with or without a website, closed places, exact name match, inside the location only.
- Clear errors. Searches that find nothing, unknown locations and invalid IDs return an error row instead of failing the run. Each place is delivered once per run, even when several searches find it.
- Built for automation. Run it on a schedule, call it from the API, or connect it to an AI agent through the Bytewells MCP server.
Why it is fast and cheap
It reads the same data endpoints the Google Maps website loads in your browser, over plain HTTP, without running a browser. One request returns up to ~150 places with all their fields, so 1,000 places take a handful of requests and a few seconds. That is why a run uses a fraction of the compute of browser-based scrapers.
What data can you extract from Google Maps?
| Field | Example | What it is |
|---|---|---|
title | Italala Caffè | Business name |
categoryName, categories | Coffee shop | Main category, and all of them |
address, street, city, postalCode, state, countryCode | Vokiečių g. 1, Vilnius… | Full address and its parts |
phone, phoneUnformatted | (0-630) 33363, 063033363 | Local and international format |
website | https://www.italala.lt/ | The business's own site |
totalScore, reviewsCount | 4.7, 1519 | Average rating and number of reviews |
openingHours, additionalOpeningHours | [{"day":"Monday","hours":"8 am–8 pm"}] | Hours for each weekday; delivery, kitchen and happy hours |
location | {"lat":54.678,"lng":25.286} | Coordinates of the pin |
placeId, cid, fid, kgmid | ChIJ6-1bDxWU3UYRue3sXBtJaiQ | Google's IDs (Places API Place ID included) |
url | https://www.google.com/maps/search/?api=1&… | Opens the place on Google Maps |
description | Bread-themed branch of the famed Italian market… | Google's editorial summary |
additionalInfo | {"Accessibility":[{"Wheelchair accessible entrance":true}]} | Attributes and the owner's highlights |
permanentlyClosed, temporarilyClosed | false | Closed status |
claimThisBusiness | false | true when no owner manages the listing: a local-marketing lead |
reserveTableLinks, orderOnlineLinks | [{"name":"opentable.com","url":…}] | Booking and ordering links |
imageUrl, imagesCount, imageUrls | 1183 | Main photo, number of photos, photo URLs |
searchString, searchLocation, rank, insideSearchArea | coffee shop, 2 | Which search found the place, and where |
emails, phones, facebooks, instagrams, linkedIns, twitters, youtubes, tiktoks, pinterests | ["[email protected]"] | Contacts add-on: from the business's website |
reviewsDistribution, reviewsTags, reviews, plusCode, ownerDescription, hotelStars | {"fiveStar":1266,…} | Place details add-on |
How to scrape Google Maps places
- Click Try for free. Type what you are looking for in Search terms ("plumber", "coffee shop") and where in Location ("Austin, Texas, USA").
- Set Places per search. It also caps how much compute the run uses.
- Turn on the add-ons you need, then click Start. 1,000 places take about a minute.
- Open the Output tab (views: Overview, Contacts / leads, Location, Ratings, Top reviews, Opening hours), or read the dataset as JSON.
How much does it cost to scrape Google Maps?
One flat monthly rental, shown on this page, with a free trial. There is no per-place fee: scrape as many places as you need.
Runs use your credits for compute, as every Actor does. Measured on Bytewells: 100 New York restaurants in 15 seconds at 1 GB for $0.0006 of credits, and 20 Austin dentists with place details, reviews and website contacts in 10 seconds for $0.0004. Google Maps answers without a proxy, so compute is the whole cost: about $0.01 per 1,000 places, $0.02 with details and contacts.
Input
{
"searchStringsArray": ["dentist", "orthodontist"],
"locationQuery": "Austin, Texas, USA",
"maxCrawledPlacesPerSearch": 500,
"website": "withWebsite",
"scrapeContacts": true
}
| Field | Default | Notes |
|---|---|---|
Search terms (searchStringsArray) | restaurant | One per line; each is searched in every location |
Location (locationQuery) | New York, USA | One per line; split into map cells for full coverage |
Places per search (maxCrawledPlacesPerSearch) | 100 | Per term and location: your main cost lever |
Max places in total (maxItems) | none | Optional cap for the whole run |
Language (language) | en | Language of categories, hours and summaries |
| Country, state, county, city, postal code | none | Instead of Location, as separate fields |
Custom search area (customGeolocation) | none | GeoJSON polygon, or a point with radiusKm |
Google Maps URLs (startUrls), Place IDs (placeIds) | none | Single places, or a search link with its map view |
| Filters | off | Category, minimum rating, website, closed, exact name, inside the location (on) |
Place details (scrapePlaceDetailPage) | off | Star breakdown, review topics, top reviews, plus code, owner description |
Contacts from website (scrapeContacts) | off | Emails, phones and social profiles from the business's site |
Image URLs per place (maxImages) | 0 | Photo URLs in imageUrls |
| Proxy | automatic | Leave empty: the Actor picks the address itself |
Output
Each place is one row. The Overview view shows the key columns; every export includes all fields. With place details on, reviews holds the top reviews:
{
"stars": 5,
"text": "Everything was absolutely perfect. The service, pastries, and drinks were 11/10…",
"publishedAtDate": "2026-05-17T15:30:22.997Z",
"reviewerName": "Austeja G",
"isLocalGuide": true,
"reviewDetailedRating": { "Food": 5, "Service": 5, "Atmosphere": 5 },
"reviewContext": { "Price per person": "€10–15", "Reservation": "Reservations not required" }
}
A search, URL or Place ID that cannot be read becomes a row with "recordType": "error" instead of failing the run:
errorCode | Meaning |
|---|---|
LOCATION_NOT_FOUND | The location is not on the map. Write it as "City, Country" |
NOT_FOUND | No place for this Place ID, CID or link |
BAD_AREA | The custom search area is not valid GeoJSON |
BLOCKED, RATE_LIMITED, UNREACHABLE | Google refused or did not answer; run again |
PARSE_FAILED | Google changed its format; please report it |
The run's RUN_SUMMARY record has the totals: places, map views, places each filter dropped, and why the run stopped.
Tips and limits
- Different terms find different places. "plumber" and "electrician" add places; "plumber" and "plumbers" mostly find the same ones.
- "Only places inside the location" is on by default. A city's map rectangle also covers its neighbours; turn it off to keep them (each row has
insideSearchArea). - Reviews: the top ones only. Google shows up to 5 reviews per place to visitors who are not signed in, and this Actor never signs in. Full review history, popular times and price levels are only shown to signed-in Google users, so they are not included.
- Attributes are the groups Google's website shows to visitors who are not signed in (accessibility and the owner's highlights).
- Review counts: Google sometimes hides them from visitors who are not signed in. The Actor detects that and asks again from another session, within a set number of retries per run; where Google keeps hiding it,
reviewsCountis null and the run log says how many.
FAQ
Is it legal to scrape Google Maps?
This Actor collects business listings that Google Maps shows to anyone, plus what businesses publish on their own websites. It does not log in. Reviewer names are personal data under the GDPR: turn off Include reviewer names if you do not need them, and check with a lawyer what your use case allows. Google Maps is a trademark of Google LLC; this Actor is not affiliated with or endorsed by Google.
Can I use it through the API?
Yes. Start runs from the API, schedule them, and read the dataset as JSON. The output schema links the places, the leads view and the run summary.
Can AI agents use it?
Yes, through the Bytewells MCP server. The input fields and the Store description are written so an agent can fill them in from a request like "dentists in Austin with emails".
I switched from compass/crawler-google-places. Do I need to change my input?
Usually not: searchStringsArray, locationQuery, maxCrawledPlacesPerSearch, language, countryCode, city, state, county, postalCode, customGeolocation, startUrls, placeIds, the filters, scrapePlaceDetailPage and scrapeContacts work the same, and the output uses the same field names. Inputs for features this Actor does not have (full review history, people leads) are ignored.
Why did I get fewer places than I asked for?
The area holds fewer places for that term, filters dropped some, several searches found the same places (each is delivered once), or the run hit its timeout. A run also stops early, with everything found so far saved, when Google keeps answering with places it already found, places outside the area or places your filters remove: splitting a big area or a long list of similar terms across runs gets the rest. RUN_SUMMARY says which (stoppedBy).
FAQ
- What data does it return for each place?
- Name, address, phone, website, rating, review count, opening hours, categories and coordinates. Optional: place details, the top reviews, and e-mails and social profiles found on the business's website.
- Can it cover a whole city?
- Yes. Give a keyword and a location; the area is searched cell by cell so a whole city is covered, not only the first screen of results. Only places inside the location are kept by default, and each row has insideSearchArea.
- How much does it cost?
- One flat monthly rental, shown on this page, with a free trial and no per-place fee. Google Maps answers without a proxy, so compute is the whole cost. Measured on Bytewells: 100 New York restaurants in 15 seconds for $0.0006, about $0.01 per 1,000 places, $0.02 with details and contacts.
- Does it get all reviews of a place?
- No. Google shows up to 5 reviews per place to visitors who are not signed in, and this Actor never signs in. Full review history, popular times and price levels are shown only to signed-in users.
- I use compass/crawler-google-places. Do I need to change my input?
- Usually not. searchStringsArray, locationQuery, maxCrawledPlacesPerSearch, language, countryCode, city, state, postalCode, startUrls, placeIds, the filters, scrapePlaceDetailPage and scrapeContacts work the same, and the output uses the same field names.
- Why did I get fewer places than I asked for?
- The area holds fewer places for that term, filters dropped some, several searches found the same places (each is delivered once), or the run reached its timeout. RUN_SUMMARY says which (stoppedBy).
- Can I use it through the API or from AI agents?
- Yes. Start runs through the Bytewells API (bytewells.com/docs/api), schedule them and read the dataset as JSON; webhooks work too. The API is Apify-compatible, so apify-client works with the Bytewells base URL. AI agents can run it as a tool through the Bytewells MCP server (bytewells.com/docs/mcp).
Input
Preview of the input form. Rent the Actor to fill it in and start runs from the console.
searchStringsArrayWhat you would type into Google Maps: "dentist", "coffee shop", "plumber". One term per line; each is searched in every location below. Different trades find different places; plural variants of one word ("plumber", "plumbers") mostly find the same ones again. A business name works too ("Joe's Pizza").
locationQueryWhere to search: a city, district, region or country, as "City, Country" ("Austin, Texas, USA"). One per line to search several. The whole area is covered: it is split into map cells, so a city returns far more than Google's ~120 results per search. Leave empty to let Google go by the search words alone.
maxCrawledPlacesPerSearchStop each search (one term in one location) after this many places. You pay per place, so this is your main cost lever. 100 is a quick list; a large city holds thousands for a broad term like "restaurant". Places a filter drops do not count.
maxItemsOptional cap for the whole run, across all searches, URLs and Place IDs. Empty = no cap beyond "Places per search" and your maximum charge per run.
languageThe language Google answers in: category names, opening hours, editorial summaries. Place names and addresses stay as the business wrote them.
Use these instead of "Location" if you prefer fields. A custom area (polygon or radius) overrides both.
countryCodeOptional. With no Location set, searches the whole country (or the state, county, city or postal code below). With a Location, keeps that location inside this country and ranks results for it.
stateOptional: a state or region, e.g. "Massachusetts". Used when "Location" is empty.
countyOptional: a county or district, e.g. "New York County". Used when "Location" is empty.
cityOptional: a city, e.g. "Pittsburgh" (state and country go in their own fields). Used when "Location" is empty.
postalCodeOptional: one postal code, e.g. "10001". Combine it with Country. Used when "Location" is empty.
customGeolocationAn exact area instead of a named place, as GeoJSON with [longitude, latitude] pairs: a Polygon, MultiPolygon, Feature(Collection), or a Point with "radiusKm", e.g. {"type":"Point","coordinates":[-74.006,40.7128],"radiusKm":2}. Draw one at geojson.io. Overrides Location and the fields above.
startUrlsGoogle Maps links, one per line. A place (…/maps/place/…, share links maps.app.goo.gl/…, ?cid=…) is scraped as that one place; a search page (…/maps/search/coffee/@60.17,24.94,13z) is scraped as that search in that map view.
placeIdsPlace IDs from the Places API or an earlier run ("ChIJreV9aqYWdkgROM_boL6YbwA"), one per line. CIDs ("7838756667406262025") and feature ids ("0x89c3…:0x6cc8…") work too.
Filters are free: a place a filter drops is never charged and does not count toward "Places per search".
categoryFilterWordsKeep only places whose Google category contains one of these words: "dentist" keeps "Dentist" and "Cosmetic dentist". Case and accents do not matter. Empty = all categories.
searchMatchingKeep only places whose name contains, or equals, the search term. Handy for every branch of a chain ("Starbucks"). Case, accents and punctuation do not matter.
placeMinimumStarsKeep only places rated at least this many stars. Places without a rating are dropped when this is set.
websiteKeep places with a website (for contact enrichment) or without one (leads for web design and marketing).
skipClosedPlacesDrop places Google marks as permanently or temporarily closed.
onlyInsideAreaDrop places outside the location's boundary (from OpenStreetMap), such as the next town that falls inside the map rectangle. Turn off to keep them; each row says insideSearchArea true or false.
scrapePlaceDetailPageOne extra request per place for the 1-5 star breakdown, review topics with counts, the top reviews Google shows, the business's own description, plus code, hotel class and the full week of hours. Charged per place where the details loaded.
scrapeContactsVisits each place's own website for emails, phone numbers and social profiles (Facebook, Instagram, LinkedIn, X, YouTube, TikTok, Pinterest): the homepage, then up to two contact pages. Never a crawl. Charged only where it finds at least one contact.
maxImagesHow many photo URLs to list per place in imageUrls (the main photo is always in imageUrl). Search results carry a few; with Place details on, more. Free.
scrapeReviewsPersonalDataWith Place details on: include each review author's name, profile link, photo and review count. Turn off to collect reviews without personal data (GDPR).
The defaults are tuned for speed and cost. Change them only if runs are blocked.
proxyConfigurationLeave empty: the Actor picks the address itself, starting with the run's own (free) and moving to Apify datacenter proxy when Google hides review counts from it. Set a proxy only to force your own; then only that one is used.
maxConcurrencyHow many searches, URLs or Place IDs are processed at the same time.
requestTimeoutSecsHow long to wait for one response before giving up on it.
maxRetriesRetries for blocked, rate-limited or failed requests. Timeouts are not retried.
API
Bytewells speaks the Apify API. Existing integrations keep working: point apify-client at https://bytewells.com/api and use your Bytewells API token (Console → Settings → API access). The Actor ID is amethyst/google-maps-scraper (or amethyst~google-maps-scraper in URLs).
JavaScript · apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({
token: process.env.BYTEWELLS_TOKEN,
baseUrl: 'https://bytewells.com/api',
});
const input = {
"searchStringsArray": [
"restaurant"
],
"locationQuery": "New York, USA",
"maxCrawledPlacesPerSearch": 100,
"language": "en",
"searchMatching": "all",
"website": "allPlaces",
"skipClosedPlaces": false,
"onlyInsideArea": true,
"scrapePlaceDetailPage": false,
"scrapeContacts": false,
"maxImages": 0,
"scrapeReviewsPersonalData": true,
"maxConcurrency": 5,
"requestTimeoutSecs": 30,
"maxRetries": 2
};
const run = await client.actor('amethyst/google-maps-scraper').call(input);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);Python · apify-client
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ["BYTEWELLS_TOKEN"], api_url="https://bytewells.com/api")
run_input = {
"searchStringsArray": [
"restaurant"
],
"locationQuery": "New York, USA",
"maxCrawledPlacesPerSearch": 100,
"language": "en",
"searchMatching": "all",
"website": "allPlaces",
"skipClosedPlaces": False,
"onlyInsideArea": True,
"scrapePlaceDetailPage": False,
"scrapeContacts": False,
"maxImages": 0,
"scrapeReviewsPersonalData": True,
"maxConcurrency": 5,
"requestTimeoutSecs": 30,
"maxRetries": 2
}
run = client.actor("amethyst/google-maps-scraper").call(run_input=run_input)
items = client.dataset(run["defaultDatasetId"]).list_items().items
print(items)HTTP · curl
curl -X POST "https://bytewells.com/api/v2/acts/amethyst~google-maps-scraper/runs" \
-H "Authorization: Bearer $BYTEWELLS_TOKEN" \
-H "Content-Type: application/json" \
-d '{"searchStringsArray":["restaurant"],"locationQuery":"New York, USA","maxCrawledPlacesPerSearch":100,"language":"en","searchMatching":"all","website":"allPlaces","skipClosedPlaces":false,"onlyInsideArea":true,"scrapePlaceDetailPage":false,"scrapeContacts":false,"maxImages":0,"scrapeReviewsPersonalData":true,"maxConcurrency":5,"requestTimeoutSecs":30,"maxRetries":2}'Runs are billed as compute from your credit balance. Paid Actors need an active rental or trial; otherwise the API answers 402 rental-required.
Related
More Actors from the same publisher.

Facebook Ads Library Scraper - Meta Ads, All Countries
Eimantas V · @amethyst
Scrape the Facebook Ads Library in any country: ad copy, images, videos, landing pages, CTAs, run dates and advertiser Pages. No login, no token needed.
MarketingSocial mediaBusiness$29/mo7-day free trial
Facebook Pages Scraper - Emails, Phones & Followers
Eimantas V · @amethyst
Scrape Facebook Pages by link, username or ID: email, phone, website, address, exact followers, category, rating and social links. No login or cookies.
Lead generationSocial mediaMarketing$19/mo7-day free trial
Facebook Posts Scraper - Pages, Profiles & Reactions
Eimantas V · @amethyst
Scrape posts from public Facebook Pages and profiles: text, date, reactions by type, comments, shares, video views and media. Date filters and new-post alerts.
Social mediaMarketingLead generation$19/mo7-day free trial
Google Search Results Scraper - SERP API
Eimantas V · @amethyst
Scrape Google search results by keyword: organic results with ranks, places, top stories, knowledge panel, related searches, News and Videos. Any country.
SEO toolsMarketingLead generation$19/mo7-day free trial
Instagram Profile Scraper - Followers, Posts & Emails
Eimantas V · @amethyst
Scrape Instagram profiles by username or URL: exact followers, following and posts, bio, links, emails and phones from bios, engagement rate, latest posts.
Social mediaLead generationMarketing$19/mo7-day free trial
Instagram Search Scraper - Users, Hashtags, Places
Eimantas V · @amethyst
Search Instagram by keyword without logging in: profiles with exact followers, hashtags with post counts, places with addresses, and popular reels.
Social mediaMarketingLead generation$19/mo7-day free trial