Google Maps Scraper – Places, Phone, Website, Email & Hours avatar

Google Maps Scraper – Places, Phone, Website, Email & Hours

Pricing

from $1.00 / 1,000 place scrapeds

Go to Apify Store
Google Maps Scraper – Places, Phone, Website, Email & Hours

Google Maps Scraper – Places, Phone, Website, Email & Hours

Extract business data from Google Maps by search query or coordinates: name, address, phone, website, rating, hours and more.

Pricing

from $1.00 / 1,000 place scrapeds

Rating

0.0

(0)

Developer

SERP King

SERP King

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

9 minutes ago

Last modified

Share

Extract structured business data from Google Maps by search query or by coordinates: name, address, phone, website, rating, review count, opening hours, price level, and more. This actor calls a licensed SERP data provider directly — no proxies, no headless browser, no IP bans, no CAPTCHAs. Results typically come back in a few seconds per query, and the same low, transparent price applies whether you scrape 10 places or 10,000.

What data you get

Each result item includes:

  • placeId, cid — Google's stable identifiers for the place
  • name, category, additionalCategories — business name and category tags
  • address, addressParts (street, city, zip, region, country code)
  • phone, website, domain
  • rating, reviewsCount, ratingDistribution (1–5 star breakdown)
  • priceLevel
  • latitude, longitude
  • openingHours — per weekday, list of {open, close} time ranges
  • currentStatus (e.g. open now / closed / temporarily closed)
  • isClaimed — whether the business owner has claimed the listing
  • mainImageUrl, totalPhotos
  • googleMapsUrl — direct link to the place on Google Maps
  • bookOnlineUrl
  • rank — position in the search results for its query
  • query, scrapedAt (ISO 8601 UTC)

Why this scraper

  • Fast — results come from a real-time SERP data pipeline, typically seconds per query, not minutes of browser automation.
  • Cheap — pay only for places actually scraped, a flat $0.001 per place regardless of your Apify plan (see Pricing).
  • Up to 700 results per query — go well past Google's default page of ~20 results.
  • Coordinate-based search — target a specific map viewport (lat,lng,zoom) instead of relying on a text query alone, useful for dense areas or search_this_area-style workflows.
  • Optional reviews, filters, and bulk category x city expansion — see the Reviews, Filters, and Bulk categories x cities sections below.

How it compares

Public Apify Store place prices on the Free plan tier, checked against the Store API on 8 Sep 2026:

ActorPlace price (Free plan)Notes
compass/crawler-google-places$0.004/place594,600 total users, 1,814 reviews (4.70★).
vortex_data/google-maps$0.001/place712 users in the last 30 days, 18 reviews (4.84★).
This actor (moyadata/google-maps-places-scraper)$0.001/placeNo proxies, no browser, Standby API for synchronous HTTP calls, filters applied before billing (see Filters).

compass/crawler-google-places also offers GeoJSON polygon-area search and full photo-gallery extraction that this actor does not.

Pricing

This actor uses Apify's pay-per-event pricing, flat across all plans. You are charged for:

EventWhat it meansPrice
apify-actor-startApify's platform-billed "run started" event, charged once per run automatically -- no code call needed, so a run with zero results costs almost nothing$0.00005
apify-default-dataset-item ("Place scraped" in Console)Charged once per place written to the output dataset$0.001
review-scrapedCharged once per review fetched, only when includeReviews is on (see Reviews)$0.0005
contact-enrichedCharged once per place, only when enrichContacts found at least one email or social link (see Contact enrichment)$0.0015

Worked example: scraping 1,000 places costs 1,000 × $0.001 + $0.00005 ≈ $1.00. The same 1,000 places with includeReviews on and 10 reviews fetched per place cost 1,000 × $0.001 + 10,000 × $0.0005 + $0.00005 ≈ $1.00 + $5.00 = $6.00.

Apify's free plan includes monthly platform credit that, on its own, covers roughly the first 5,000 places scraped with this actor before you'd need to add a payment method.

Input

Set country to the country your queries refer to. Tested example: dentista en Providencia, Santiago with country: "US" returns places from the wrong country; with country: "CL" it returns 100 dentists in Providencia.

Minimal example:

{
"queries": ["coffee shops in Austin, TX"],
"country": "US",
"language": "en",
"maxResultsPerQuery": 20
}
FieldTypeDefaultDescription
queriesarray of strings["coffee shops in Austin, TX"]One or more search queries, e.g. "dentist in Austin, TX". Max 700 characters each. Optional if both categories and cities are set (see Bulk categories x cities). At least one query must result, from queries and/or expansion.
countrystring (ISO-2)"US"Country to search in. Ignored for query matching when coordinates is set, but always used to resolve the location for review requests (see Reviews).
languagestring"en"Language code for the results (e.g. "en", "es", "de"). Also selects the categories x cities query template.
coordinatesstringOptional "lat,lng,zoom" (e.g. "30.2672,-97.7431,14"). When set, search is anchored to this map viewport instead of country.
maxResultsPerQueryinteger20 (max 700)Maximum number of places returned per query.
deduplicatebooleantrueRemove duplicate places (by placeId) seen across multiple queries in the same run.
categoriesarray of stringsBulk mode: business categories to cross with cities. See Bulk categories x cities.
citiesarray of stringsBulk mode: cities/areas to cross with categories. See Bulk categories x cities.
includeReviewsbooleanfalseFetch reviews for each place. See Reviews.
maxReviewsPerPlaceinteger10 (10-100)Reviews to fetch per place when includeReviews is on. See Reviews.
reviewsSortstring"newest"One of relevant, newest, highest_rating, lowest_rating. See Reviews.
minRatingnumberKeep only places with a rating >= this value (0-5). See Filters.
minReviewsintegerKeep only places with at least this many reviews. See Filters.
hasWebsitebooleantrue/false to require or exclude a website. See Filters.
hasPhonebooleantrue/false to require or exclude a phone number. See Filters.
openNowbooleantrue/false to require or exclude "currently open". See Filters.
categoriesIncludearray of stringsKeep only places matching one of these categories (case-insensitive, exact match). See Filters.
enrichContactsbooleanfalseFetch emails and social links from each place's website. See Contact enrichment.

Output

One real dataset item, from a live run against plumbers in Austin, TX on 8 Sep 2026 (see the measured-yield numbers in Contact enrichment below for the full run this item came from):

{
"placeId": "ChIJpxUGhC_LRIYR2g941oH50ow",
"cid": "10147447246463438810",
"name": "Beyond Wow Plumbing & Drains",
"category": "Plumber",
"additionalCategories": [
"Drainage service",
"Gas installation service",
"Hot water system supplier",
"Water filter supplier"
],
"address": "3432 Greystone Dr, Austin, TX 78731",
"addressParts": {
"street": "3432 Greystone Dr",
"city": "Austin",
"zip": "78731",
"region": "Texas",
"countryCode": "US"
},
"phone": "+1512-601-6173",
"website": "https://beyondwow.com/?utm_campaign=gmb&utm_medium=gbp-austin&utm_source=google",
"domain": "beyondwow.com",
"rating": 4.9,
"reviewsCount": 2579,
"ratingDistribution": { "1": 58, "2": 17, "3": 25, "4": 50, "5": 2429 },
"priceLevel": null,
"latitude": 30.3578911,
"longitude": -97.7482485,
"openingHours": {
"monday": [{ "open": "07:00", "close": "18:00" }],
"tuesday": [{ "open": "07:00", "close": "18:00" }],
"wednesday": [{ "open": "07:00", "close": "18:00" }],
"thursday": [{ "open": "07:00", "close": "18:00" }],
"friday": [{ "open": "07:00", "close": "18:00" }]
},
"currentStatus": "open",
"isClaimed": true,
"mainImageUrl": "https://lh3.googleusercontent.com/gps-cs-s/AHRPTWmiDLztnnpPJfoaMPrurchultIC5ltxZTId2hco9G7k_JQt-bniJ6Ujl8eMC4IUEmE9JkRzjy28-LKlqRjzNYJ5-lfpiDCCuhaa7oNEBwjLc6YrBqSgiMoPu83s4KuzBnYkmd8=w408-h408-k-no",
"totalPhotos": 247,
"googleMapsUrl": "https://www.google.com/maps?cid=10147447246463438810",
"bookOnlineUrl": "https://form.recreateai.com/?rai_pak=8b0f7a2c-e37c-4714-bdff-999dc19da556&branch_id=a5321498-4798-41c9-b352-0814630c5cfe&rwg_token=AE37R_h4RLz_FFoarFAgEf7DCB9ApToHo2LMDEU4WbzMvDUtjVTiP69iCQVdIZjuf5BRWfYElxHuzQJIspr7CGNboOSgSOia0Q%3D%3D",
"rank": 1,
"query": "plumbers in Austin, TX",
"scrapedAt": "2026-09-08T17:08:09Z",
"reviews": null,
"contacts": {
"emails": ["info@beyondwow.com"],
"socials": {
"facebook": "https://www.facebook.com/beyondwowplumbing",
"instagram": "https://www.instagram.com/beyondwowplumbing",
"linkedin": "https://www.linkedin.com/company/beyondwowplumbing",
"twitter": null,
"tiktok": "https://www.tiktok.com/@beyondwowplumbing",
"youtube": "https://www.youtube.com/@beyondwowplumbing",
"whatsapp": null
},
"phonesOnSite": []
},
"contactsStatus": "enriched"
}
FieldTypeDescription
placeIdstringGoogle's place identifier
cidstringGoogle's numeric customer/place ID, used to build googleMapsUrl
namestringBusiness name
categorystringPrimary category
additionalCategoriesarray of stringsSecondary category tags
addressstringFull formatted address
addressPartsobjectstreet, city, zip, region, countryCode
phonestring | nullPhone number as shown on the listing
websitestring | nullBusiness website URL
domainstring | nullWebsite domain, extracted from website
ratingfloat | nullAverage rating (0–5), null if the place has no ratings
reviewsCountinteger | nullTotal number of reviews
ratingDistributionobject | nullCount of reviews per star, keys "1""5"
priceLevelstring | nullOne of inexpensive, moderate, expensive, very_expensive, or null when Google shows no price level
latitudefloatLatitude
longitudefloatLongitude
openingHoursobject | nullPer weekday list of {open, close} ranges ("HH:MM")
currentStatusstring | nulle.g. open, closed, temporarily_closed
isClaimedbooleanWhether the owner has claimed the listing
mainImageUrlstring | nullURL of the listing's main photo
totalPhotosinteger | nullTotal photo count on the listing
googleMapsUrlstringDirect Google Maps link for the place
bookOnlineUrlstring | nullOnline booking link, if available
rankintegerPosition of this place in the results for its query
querystringThe input query that returned this place
scrapedAtstringISO 8601 UTC timestamp of when the place was scraped
reviewsarray of objects | nullnull unless includeReviews is true, in which case it's always an array (possibly empty). See Reviews.

Reviews

Set includeReviews: true to fetch reviews for each place, in addition to the place data above.

FieldTypeDefaultDescription
includeReviewsbooleanfalseTurns reviews on.
maxReviewsPerPlaceinteger10Reviews to fetch per place (10-100; multiples of 10 recommended — Google's reviews API pages in tens).
reviewsSortstring"newest"One of relevant, newest, highest_rating, lowest_rating.

Each entry in the reviews array looks like:

{
"reviewId": "ChdDSUhNMG9nS0VJQ0FnSURvd2VYSkR3EAE",
"author": "Jamie Rivera",
"authorUrl": "https://www.google.com/maps/contrib/000000000000000000001",
"isLocalGuide": true,
"rating": 5,
"text": "Great coffee and friendly staff, will come back.",
"originalText": "Great coffee and friendly staff, will come back.",
"publishedAt": "2026-08-24T09:15:00Z",
"ownerAnswer": "Thanks so much for the kind words, Jamie!",
"ownerAnswerAt": "2026-08-31T10:00:00Z",
"imagesCount": 1,
"likes": null,
"reviewUrl": "https://www.google.com/maps/reviews/data=example1"
}

Notes:

  • likes is always null — Google's reviews data doesn't expose a like/thumbs-up count.
  • A place with no cid, or whose reviews couldn't be fetched (upstream error or timeout), gets reviews: [], not null — the place itself is still delivered and charged normally.
  • Reviews are billed with the review-scraped event, once per review actually fetched, charged right after the place that contains them is pushed (see Pricing). If your max charge limit is reached while fetching reviews, this actor stops requesting reviews but keeps delivering places (charged only apify-default-dataset-item) until its own limit is reached.
  • Because each batch of review requests can take up to a minute upstream, a run with includeReviews on is capped at 2,000 places total (maxResultsPerQuery x queries) — the run fails validation up front if your input would exceed that.

Filters

Six optional fields filter results after scraping and before truncating to maxResultsPerQuery or charging apify-default-dataset-item — you only pay for places that pass every filter you set.

FieldTypeBehavior
minRatingnumber (0-5)Keep places with rating >= minRating. Places with no rating are excluded.
minReviewsintegerKeep places with reviewsCount >= minReviews. Places with no review count are excluded.
hasWebsitebooleantrue keeps only places with a website; false keeps only places without one.
hasPhonebooleantrue keeps only places with a phone number; false keeps only places without one.
openNowbooleantrue keeps only places with currentStatus == "open"; false keeps only the rest.
categoriesIncludearray of stringsKeep places whose category or any additionalCategories entry case-insensitively matches one of these (exact match, not substring).

STATS (placesFiltered in the run's key-value store) reports how many scraped places were dropped by filters.

Bulk categories x cities

Instead of (or in addition to) writing out queries by hand, fill both categories and cities to auto-generate every combination:

{
"categories": ["dentist", "coffee shop"],
"cities": ["Austin, TX", "Denver, CO"],
"language": "en"
}

generates, category-major: "dentist in Austin, TX", "dentist in Denver, CO", "coffee shop in Austin, TX", "coffee shop in Denver, CO". The template depends on language: "{category} in {city}" for en, "{category} en {city}" for es, and "{category} {city}" for anything else. Generated queries are concatenated with any explicit queries, then deduplicated together with them. A run is capped at 500 total queries — the input fails validation up front if categories x cities (plus any explicit queries) would exceed that.

Limits

  • Google Maps listings themselves don't expose email addresses (review authors are identified by display name only). With enrichContacts on, the actor reads the business's own website to find emails and social profiles — see Contact enrichment.
  • Paid/ad results (maps_paid_item) are skipped; only organic listings are returned.
  • Maximum 700 results per query — this is the data provider's ceiling; most queries have far fewer organic listings anyway.
  • Results reflect Google Maps at the moment the query runs; Google's own index and ranking can change between runs.
  • includeReviews caps a run at 2,000 places total (see Reviews).

FAQ

Is this legal? The actor retrieves publicly available business listing data through a licensed SERP data provider, the same category of data source used by SEO and local-search tools. You are responsible for how you use the extracted data under your local regulations (e.g. marketing/consent rules for phone numbers).

How is this different from other Google Maps scrapers on Apify? Most Google Maps actors drive a real or headless browser against Google Maps, which means proxy costs, occasional IP blocks/CAPTCHAs, and slower runs. This actor calls a licensed SERP data API directly: no proxies, no browser, no IP bans, and — because there's no proxy or browser compute overhead — a lower price per place.

How do I search by coordinates instead of a text query? Set the coordinates input field to "lat,lng,zoom" (e.g. "30.2672,-97.7431,14"). When present, it overrides country and anchors the search to that map viewport, similar to using "Search this area" on Google Maps itself.

Why did I get fewer results than maxResultsPerQuery? Google Maps itself caps how many organic results exist for a given query and area — if a query only has 12 relevant places, that's what you'll get regardless of the requested maximum. Narrowing the query (e.g. adding a neighborhood) or using coordinates with "search this area"-style bounding often surfaces more results than a broad city-wide text query.

Can I run this via API, on a schedule, or trigger it from a webhook? Yes. Any Apify actor can be started, monitored and read back through the Apify API, run on a recurring schedule from the Apify Console, and wired to webhooks on run events (started, succeeded, failed) from the same Console.

Contact enrichment

Set enrichContacts: true (default false) to have the actor visit each result's website and pull out public contact details: email addresses, social media links, and phone numbers found on the page.

For every place with a website, the actor fetches the home page and up to two candidate pages likely to list contact details (/contact, /contacto, /about, or a link on the home page whose text contains "contact"). Each result item gets two new fields:

  • contacts{ "emails": [...], "socials": { "facebook", "instagram", "linkedin", "twitter", "tiktok", "youtube", "whatsapp" }, "phonesOnSite": [...] }, or null when nothing was fetched.
  • contactsStatus — one of:
StatusMeaning
enrichedThe site was fetched and contacts was extracted (may still be empty if the site has none of the above).
no_websiteThe place had no website field, so no request was made.
blockedThe site returned an anti-bot response (HTTP 403/503) or its robots.txt disallows the pages this actor requests.
timeoutThe site did not respond within 10 seconds (one retry is attempted, then it's reported as a timeout).
errorAny other network or HTTP error (e.g. a 500 response, too many redirects).
unsafe_urlThe website value failed a safety check (not http(s), a bare IP address, or an address that resolves to a private/internal network) and was never requested.

Measured yield. In one test run (plumbers in Austin, TX, 100 places, 8 Sep 2026): 98 places had a website and 2 had none. Of those 98, enrichment found at least one email for 57 places and at least one social profile for 68; 4 sites blocked the fetch and 5 errored. 78 places were charged the contact-enriched event (at least one email or social found). The whole run — 100 places plus enrichment — took 81 seconds. Results vary by industry and country; this is one data point, not a guarantee.

Pricing. The contact-enriched event is charged once per place, only when at least one email address or social media link was found — never merely for having attempted a fetch. Proposed price: $0.0015 per enriched place, same across all plans.

Limits, by design:

  • No JavaScript is executed — sites that render their contact details client-side (JS-only) won't be enriched even if a human visitor could see them.
  • Sites behind anti-bot protection return contactsStatus: "blocked" rather than being retried aggressively or routed through proxies. This actor never uses proxies and never retries more than once.
  • robots.txt is respected: if a site disallows the pages this actor would request, they are skipped.
  • Each page fetch is capped at 1 MB and 10 seconds; requests use an identified User-Agent (GoogleMapsPlacesScraper/0.2).

API mode (Standby)

Standby is enabled on this actor as of September 2026; its base URL is https://moyadata--google-maps-places-scraper.apify.actor.

This actor can also run as a synchronous HTTP API instead of a queued run, using Apify's Standby mode. Standby must be turned on for this actor in Apify Console (Settings -> Standby) by the developer publishing it — it is off by default and cannot be enabled from the input.

Once Standby is enabled, the actor's Standby URL (shown on the actor's Console page) accepts:

  • GET /search?query=...&country=...&language=...&coordinates=...&maxResults=...
  • POST /search with a JSON body using the same fields as the actor input (only places are returned in Standby mode — no reviews, no contact enrichment).

Both return 200 with { "places": [...], "stats": {...} } on success, a 400 with { "error": "..." } on invalid input, and a 503 with { "error": "..." } if the upstream data provider is unavailable (balance exhausted, circuit breaker open).

Example:

curl "https://moyadata--google-maps-places-scraper.apify.actor/search?query=coffee+shops+in+Austin,+TX&country=US&maxResults=20" \
-H "Authorization: Bearer $APIFY_TOKEN"
curl -X POST "https://moyadata--google-maps-places-scraper.apify.actor/search" \
-H "Authorization: Bearer $APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"queries": ["coffee shops in Austin, TX"], "country": "US", "maxResultsPerQuery": 20}'

Pricing. Standby requests are charged the same apify-default-dataset-item event ("Place scraped" in Console) as a normal run, per place returned in the response — there is no separate "per request" charge and no dataset is written (there's nothing to push, so the event is charged directly). apify-actor-start is billed once by the platform when the Standby run starts, not per individual request.