Google Maps Scraper - User friendly
Pricing
from $2.00 / 1,000 results
Google Maps Scraper - User friendly
Pricing
from $2.00 / 1,000 results
Rating
0.0
(0)
Developer
yourlocalhost
Maintained by CommunityActor stats
0
Bookmarked
1
Total users
0
Monthly active users
2 days ago
Last modified
Categories
Share
Easy Google Maps Places Scraper
Scrape Google Maps places — businesses, points of interest, their ratings, contact details and more — by search term, location, Google Maps URL or place ID. Built to be the easiest Maps scraper to pick up, with the features other scrapers charge extra for included for free.
First run in 10 seconds: type a search term, type a location, hit Start. Everything else has sensible defaults.
Why this actor
| Capability | This actor | Typical Maps scrapers |
|---|---|---|
| Beat the ~120-results-per-area limit | ✅ Automatic adaptive tiling | ⚠️ Manual GeoJSON required |
| Many locations in one run | ✅ Built in, one merged dataset | 💰 Paid orchestrator add-on |
| Cross-search de-duplication | ✅ Built in, free | 💰 Often a paid step |
| Incremental / delta runs | ✅ Built in | ❌ Rare |
| Contact enrichment (emails, socials) | ✅ Bundled | 💰 Separate paid actor |
| Interactive results map | ✅ results-map.html | ❌ |
| Beginner-safe cost caps | ✅ Hard maxTotalPlaces cap | ⚠️ Easy to overspend |
Quick start
- Open the actor in the Apify Console.
- Fill only the three default fields:
- Search terms — e.g.
coffee shop - Location — e.g.
Boulder, Colorado, USA - Max places per search term — keep it small (e.g.
50) for your first run.
- Search terms — e.g.
- Click Start. Results appear in the Dataset tab; an interactive map is
saved to the Storage → Key-value store as
results-map.
Everything else lives in collapsible Advanced sections — open them only when you need reviews, enrichment, incremental mode or custom areas.
Presets
Pick a Preset to auto-configure the actor for a common job. A preset only fills fields you have not set yourself.
| Preset | What it does |
|---|---|
| Lead generation | Contact enrichment ON, larger limits — turn places into leads. |
| Review analysis | Reviews ON (100/place), balanced limits. |
| Quick scan | Tiny, fast, cheap run — listing data only. |
Input
Always visible
| Field | Type | Default | Description |
|---|---|---|---|
searchTerms | string[] | — | What to look for, e.g. ["dentist","orthodontist"]. |
location | string | — | Free-text place. Resolved automatically — no polygon needed. |
maxPlacesPerSearch | integer | 50 | Cap per search term. |
Advanced (collapsed by default)
- Location targeting —
locations[](native multi-location), structuredcountry/state/city/postalCode,startUrls[](direct Google Maps URLs),placeIds[], andcustomGeolocation(optional GeoJSON / circle override). - Search limits & language —
maxTotalPlaces(hard cost cap),language,regionCode. - Reviews —
maxReviews,reviewsSort. - Contact enrichment —
scrapeContactDetails,maxEnrichmentPagesPerSite. - Incremental / delta mode —
incrementalMode,onlyNewOrChanged,stateStoreName. - Personal data & compliance —
scrapePersonalData(master opt-in, see below). - Performance & browser —
listingMode(auto/http/browser),maxConcurrency,maxRequestRetries,minTileResultsToSubdivide,maxTileDepth. - Proxy —
proxyConfiguration(defaults to Apify RESIDENTIAL proxy).
You can provide any combination of searchTerms + locations, startUrls
and placeIds in a single run.
Output
Each dataset item is one place:
{"title": "Ozo Coffee Roasters","categoryName": "Coffee shop","categories": ["Coffee shop", "Cafe"],"address": "1015 Pearl St, Boulder, CO 80302, USA","street": "1015 Pearl St","city": "Boulder","state": "CO","postalCode": "80302","countryCode": "US","location": { "lat": 40.0176, "lng": -105.2797 },"plusCode": "...","phone": "(303) 544-0500","phoneUnformatted": "+13035440500","website": "https://ozocoffee.com/","totalScore": 4.6,"reviewsCount": 1234,"reviewsDistribution": { "oneStar": 10, "twoStar": 12, "threeStar": 40, "fourStar": 300, "fiveStar": 872 },"price": "$$","openingHours": [{ "day": "Monday", "hours": "7 AM–6 PM" }],"permanentlyClosed": false,"temporarilyClosed": false,"placeId": "ChIJ...","cid": "123456789012345678","fid": "0x...:0x...","url": "https://www.google.com/maps?cid=123456789012345678","imageUrl": "https://lh5.googleusercontent.com/...","additionalInfo": { "Service options": [{ "Dine-in": true }, { "Takeaway": true }] },"searchString": "coffee shop","locationQuery": "Boulder, Colorado, USA","scrapedAt": "2026-05-21T10:00:00.000Z","changeStatus": "new","emails": [],"additionalPhones": [],"socialProfiles": {},"reviews": []}
emails,socialProfilesand reviewreviewerName/reviewerIdare only populated whenscrapePersonalDatais enabled (see Compliance).changeStatus/changesare populated in incremental mode.- A
results-mapHTML file (interactive Leaflet map of every result) is written to the run's key-value store.
How adaptive tiling works
Google Maps only returns ~120 results for any single map view. To get every place in a city, this actor:
- Resolves your location to a bounding box (OpenStreetMap Nominatim).
- Searches that area as one tile.
- Only if the tile hits the ~120 cap, it splits the tile into 4 quadrants
and searches each one — recursively, up to
maxTileDepth. - Sparse tiles are never subdivided, so dense city centres get fine-grained coverage while quiet suburbs stay cheap.
You never draw a polygon. For full manual control, the advanced
customGeolocation field accepts a GeoJSON Polygon / MultiPolygon, or
{ "type": "circle", "coordinates": [lng, lat], "radiusKm": 5 }.
Incremental / delta mode
Enable incrementalMode to remember places between runs (state is stored in a
named key-value store — set stateStoreName to reuse it across a monitoring
job). Each run then classifies every place as new, changed or
unchanged, and writes a change-report to the key-value store listing new
places, rating changes, status changes and closures. Set onlyNewOrChanged to
push just the deltas to the dataset.
Contact enrichment
Enable scrapeContactDetails to visit each place's website and collect emails,
extra phone numbers and social-media profiles (Facebook, Instagram, LinkedIn,
X/Twitter, YouTube, TikTok). This is bundled at no extra charge.
Legal, privacy & compliance
Personal data is OFF by default. Reviewer names/IDs, scraped emails and
social-media profiles are only collected when you explicitly enable
scrapePersonalData. With it off, those fields are omitted even if reviews or
enrichment are enabled.
You are responsible for ensuring your use complies with Google's Terms of Service and all applicable laws (including the GDPR and CCPA). Only scrape personal data when you have a lawful basis to do so, and never use it for unsolicited bulk contact. This actor is a tool; lawful use is the operator's responsibility.
Local development
# 1. Install dependenciesnpm install# 2. Set your test input (already provided at the path below)# storage/key_value_stores/default/INPUT.json# 3. Run locally with the Apify CLIapify run --purge# Results: storage/datasets/default/# Map: storage/key_value_stores/default/results-map.html
Install the Apify CLI with npm install -g apify-cli if you do not have it.
A residential proxy is strongly recommended — Google blocks datacenter IPs.
Running locally without Apify proxy will likely be blocked; the actor will then
try the browser fallback automatically (listingMode: "auto").
Deploy to the Apify platform
apify loginapify push
Run via the Apify API
curl -X POST "https://api.apify.com/v2/acts/YOUR_USERNAME~easy-google-maps-scraper/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"searchTerms": ["coffee shop"],"location": "Boulder, Colorado, USA","maxPlacesPerSearch": 50}'
Example inputs
Multi-location lead generation
{"searchTerms": ["dentist"],"locations": ["Austin, Texas, USA", "Round Rock, Texas, USA"],"preset": "lead-generation","scrapePersonalData": true}
Monitor a city for changes
{"searchTerms": ["restaurant"],"location": "Camden, London, UK","incrementalMode": true,"onlyNewOrChanged": true,"stateStoreName": "camden-restaurants"}
Scrape specific places by ID
{"placeIds": ["ChIJ4z2Gx...", "123456789012345678"]}
Listing modes (how results are fetched)
Google Maps loads its result list through an internal endpoint whose request
format it rotates frequently. The actor therefore supports three modes via the
advanced listingMode field:
| Mode | How it works | Notes |
|---|---|---|
auto (default) | Tries the cheap HTTP endpoint first, automatically falls back to a headless browser if HTTP returns nothing. | Recommended. Always produces results. |
http | HTTP endpoint only. | Fastest/cheapest when it works, but depends on Google's current internal format — treat as experimental. |
browser | Headless Chromium only — drives the real Google Maps UI. | Most robust. This is what auto falls back to. |
In auto / browser mode each place carries the core fields (name, category,
address, rating, coordinates, place IDs, Maps URL). Fields that only exist on
the place's own page — phone, website, openingHours — are not filled
by the browser listing path; such records have partialData: true. The HTTP
path's parser (src/parsers.js) extracts the full field set, so once the HTTP
format is current again every field is populated automatically.
Pricing notes
automakes one cheap HTTP attempt per area, then uses a headless browser for the actual listing. The browser is reused across tiles, not per place.maxTotalPlacesis a hard ceiling on the whole run — set it to control spend.- Contact enrichment adds one (or a few) lightweight HTTP requests per website.
- Proxy traffic (residential, recommended for Maps) is billed by your Apify plan.
Limitations & troubleshooting
- Zero results / "Blocked by Google" — use Apify RESIDENTIAL proxy.
automode already retries with a browser; persistent blocking is almost always a proxy-quality issue. partialData: truemeans the record came from the browser listing path and lacksphone/website/openingHours(see Listing modes above).- Google Maps' internal data format is undocumented and changes occasionally.
Every field is parsed defensively, so a format change degrades individual
fields rather than crashing the run. If many HTTP-path fields come back empty,
the array indices in
src/parsers.js(parsePlaceArray) are the place to update. - Reviews extraction returns the batch embedded in the place page (best-effort). For deep, paginated review scraping use a dedicated reviews actor.
- Results are held in memory until the run finishes (bounded by
maxTotalPlaces).