Google Maps Scraper
DeprecatedPricing
$0.50 / 1,000 results
Google Maps Scraper
Deprecated[💰 $0.50 / 1K] Lightning-fast HTTP Google Maps scraper for lead generation. No browser = 5–10× cheaper. Returns 30+ fields per place: phone, website, address, owner, ratings, amenities. Quad-tree subdivision unlocks unlimited results per area. Chrome TLS impersonation bypasses Google anti-bot.
Pricing
$0.50 / 1,000 results
Rating
0.0
(0)
Developer
Kitcune Mia
Maintained by CommunityActor stats
1
Bookmarked
3
Total users
2
Monthly active users
21 days ago
Last modified
Categories
Share
Google Maps Scraper — HTTP / curl_cffi
The fastest, cheapest, most data-rich Google Maps scraper on Apify. Pure HTTP. Zero browser. ~30+ fields per place. Quad-tree subdivision breaks Google's 120-per-area limit.
Table of contents
- Why pick this scraper
- Top 10 reasons
- Quick start
- Input reference
- Output reference
- Use cases
- Tips & tricks
- Pricing
- Integrations
- Honest limitations
- FAQ
- Tech stack
- Changelog
- Support & feedback
Why pick this scraper
| This actor | Browser-based scrapers | |
|---|---|---|
| Engine | curl_cffi with Chrome TLS impersonation | Headless Chromium |
| Memory | ~512 MB | 2–4 GB |
| Speed | 51 unique places in 3.7 s (local, no proxy) | 60–120 s for the same set |
| Apify compute units | Pennies per 1 000 places | 5–10× more |
| Coverage per area | Unlimited via quad-tree subdivision | Capped at Google's ~120 per viewport |
| Fields per place | 30–46 fields | 15–25 typical |
| Anti-bot | Chrome TLS fingerprint rotation, sticky residential sessions | Headless-flag detection risk |
| Resumability | Auto-resumes after Apify migrations | Often re-scrapes from scratch |
| Built-in filters | 5 post-fetch filters (rating, website, closed, category, name match) | Often missing |
Top 10 reasons
- Blazing fast — 5–10× faster than browser-based alternatives because there's no browser to boot, render, or close
- Cheapest on Apify — no Chromium = lowest memory footprint = lowest compute-unit cost
- Quad-tree subdivision — breaks Google's hard ~120-results-per-area limit by recursively splitting saturated viewports into 4 quadrants. Scrape entire cities, not just neighborhoods
- 30–46 fields per place — including structured address parts, full amenities tree, place tags (LGBTQ+ friendly, women-owned…), owner info, entrance coordinates, current open/closed status, next opening time, hotel-specific data, menu URL, plus code, sponsored-result detection
- TLS impersonation — curl_cffi mimics real Chrome TLS fingerprints (Chrome 120/123/124/131 rotated per request) to bypass Google's anti-bot
- Sticky residential sessions — each viewport gets its own Apify session ID so all paginated requests for one logical search hit the same residential IP. Cuts "weird traffic" challenges by ~80%
- Multi-language coverage — re-search the same areas in additional languages to catch translations & regional categories you'd otherwise miss
- Multi-zoom expansion — search each seed at zoom-N..zoom+N for +30–70% extra unique places
- 5 built-in post-fetch filters — minimum stars, has-website, skip closed, category match, exact name match. Free (no extra HTTP cost)
- Resume-ready — auto-checkpoints every 30 s + on Apify's
PERSIST_STATEevent. Migrations resume without re-pushing duplicates
Quick start
Option 1 — Apify Console (no code)
- Click Try for free
- Fill in the form:
- Search terms:
["coffee", "cafe", "bakery"] - Location:
Brooklyn, New York, USA - Defaults are tuned for max coverage — leave them
- Search terms:
- Click Start
- Watch the dataset populate live in the Dataset tab
- Export as JSON / CSV / Excel / XML / RSS
Option 2 — Apify CLI
apify call your-username/google-maps-scraper-http \--input '{"searchStringsArray": ["restaurant", "bar", "cafe"],"locationQuery": "SoHo, Manhattan, New York","maxCrawledPlacesPerSearch": 500}'
Option 3 — REST API
curl -X POST "https://api.apify.com/v2/acts/your-username~google-maps-scraper-http/runs?token=YOUR_TOKEN" \-H "Content-Type: application/json" \-d '{"searchStringsArray": ["restaurant", "bar", "cafe"],"locationQuery": "SoHo, Manhattan, New York","maxCrawledPlacesPerSearch": 500,"concurrency": 8}'
Option 4 — Python SDK
from apify_client import ApifyClientclient = ApifyClient("YOUR_APIFY_TOKEN")run = client.actor("your-username/google-maps-scraper-http").call(run_input={"searchStringsArray": ["dentist", "pediatrician"],"locationQuery": "Austin, Texas","maxCrawledPlacesPerSearch": 1000,"additionalLanguages": ["es"], # bilingual coverage"placeMinimumStars": "four", # only ≥4★ places"websiteFilter": "withWebsite", # leads only})for place in client.dataset(run["defaultDatasetId"]).iterate_items():print(place["title"], place.get("phone"), place.get("website"))
Option 5 — JavaScript SDK
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_APIFY_TOKEN' });const run = await client.actor('your-username/google-maps-scraper-http').call({searchStringsArray: ['hotel', 'hostel'],locationQuery: 'Lisbon, Portugal',maxCrawledPlacesPerSearch: 300,});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items);
Input reference
What to scrape
| Field | Type | Default | Description |
|---|---|---|---|
searchStringsArray | string[] | ["restaurant"] | Search terms. Each runs across all viewports & languages. Also accepts place_id:ChIJ…. |
locationQuery | string | — | Free-text location, geocoded via OpenStreetMap. |
customGeolocation | object (GeoJSON) | — | Polygon / MultiPolygon / Point. Overrides locationQuery. |
startUrls | string[] | — | Direct google.com/maps/place/… URLs. |
placeIds | string[] | — | Google Place IDs (e.g. ChIJ…). Each is fetched directly. |
Geo composite (alternative to locationQuery)
| Field | Description |
|---|---|
countryCode | ISO country code (e.g. US, DE) |
state | State / province / region |
county | County / regional district |
city | City name (alone — no country/state) |
postalCode | Postal / ZIP code |
These are joined into one Nominatim query when locationQuery is empty.
Coverage controls
| Field | Type | Default | Description |
|---|---|---|---|
maxCrawledPlacesPerSearch | integer | 500 | Hard cap per search term. |
maxPlacesPerViewport | integer | 80 | Per-viewport cap (Google maxes at ~120). |
enableSubdivision | boolean | true | Quad-tree split when viewport saturates. |
maxSubdivisionDepth | integer | 4 | Max recursive splits (depth=4 → 256 viewports). |
zoom | integer | auto | Override seed zoom (1–21). |
multiZoomDelta | integer | 0 | Search at zoom-N..zoom+N (+30–70% extra at 3× cost). |
language | string | "en" | Google UI language (hl=). |
additionalLanguages | string[] | [] | Extra languages to re-search. |
Filters (post-fetch, free)
| Field | Type | Default | Description |
|---|---|---|---|
categoryFilterWords | string[] | [] | Keep only places whose categories contain these words. |
searchMatching | enum | "all" | all / only_includes / only_exact |
placeMinimumStars | enum | "" | two / twoAndHalf / three / … / fourAndHalf |
websiteFilter | enum | "allPlaces" | allPlaces / withWebsite / withoutWebsite |
skipClosedPlaces | boolean | false | Drop places marked permanently/temporarily closed. |
Performance & proxy
| Field | Type | Default | Description |
|---|---|---|---|
concurrency | integer | 8 | Parallel viewport searches. |
requestTimeoutSecs | integer | 15 | Per-HTTP-request timeout. |
minRequestIntervalMs | integer | 0 | Global throttle between any two requests. |
proxyConfiguration | object | residential | Apify proxy settings. |
Output reference
Every place produces one record in the default dataset, deduped by placeId across all viewports, languages, and search terms.
Sample record
{"title": "Bird & Branch Coffee Roasters","subTitle": "Family-run specialty coffee shop","description": "Signature coffee drinks including burnt marshmallow, turmeric, & nut milks…","categoryName": "Coffee shop","categories": ["Coffee shop", "Cafe", "Corporate gift supplier"],"address": "359 W 45th St, New York, NY 10036, United States","addressParts": {"street": "359 W 45th St","city": "New York","state": "New York","postalCode": "10036","neighborhood": "Manhattan","countryCode": "US"},"neighborhood": "Manhattan","formattedLocality": "New York, NY, United States","location": {"lat": 40.7602998, "lng": -73.9907758},"entranceLocation": {"lat": 40.7602896, "lng": -73.9908287},"phone": "+1 917-265-8444","phoneUnformatted": "+19172658444","website": "http://www.birdandbranch.com/","totalScore": 4.6,"openingHoursToday": {"day": "Wednesday", "hours": "7 AM–7:30 PM"},"currentStatus": "Closed · Opens 7 AM","nextOpensAt": "06:30","permanentlyClosed": false,"temporarilyClosed": false,"placeId": "ChIJTVhsxFNYwokRXgPwYnY0vgI","fid": "0x89c25853c46c584d:0x2be347662f0035e","cid": "197653116721562462","kgmid": "/g/11hbtg2w_k","url": "https://www.google.com/maps/search/?api=1&query=…&query_place_id=ChIJ…","timezone": "America/New_York","ownerName": "Bird & Branch Coffee Roasters","placeTags": ["Identifies as women-owned", "Identifies as Asian-owned"],"additionalInfo": {"Accessibility": [{"Wheelchair-accessible car park": true}],"Service options": [{"Dine-in": true}, {"Takeout": true}],"Payments": [{"NFC mobile payments": true}, {"Credit cards": true}]},"imagesCount": 944,"imageUrl": "https://lh6.googleusercontent.com/…/photo.jpg","menu": "https://slicelife.com/restaurants/…/menu","language": "en","rank": 1,"scrapedAt": "2026-04-30T15:42:18Z","isAdvertisement": false}
Hotel-specific extras
When the result is a hotel:
| Field | Description |
|---|---|
hotelStars | e.g. "4 stars" |
hotelPrice | Nightly rate (locale-dependent currency) |
hotelCheckInDate / hotelCheckOutDate | Default search dates Google used |
hotelAmenities | ["Free Wi-Fi", "Pool", "Pet-friendly"] |
longDescription | Full multi-paragraph hotel description |
images | Array of {url, title, thumbnailUrl} |
Run summary
After the run finishes, a JSON object is stored at the OUTPUT key of the default key-value store:
{"totalPlaces": 247,"uniquePlaceIds": 247,"searchTermsProcessed": ["coffee", "cafe"],"languagesUsed": ["en"],"seedViewports": 1,"completedTasks": 13,"directPlaceUrlsResolved": 0,"datasetId": "abc123…","datasetUrl": "https://api.apify.com/v2/datasets/abc123/items?clean=true&format=json","startedAt": "2026-04-30T15:40:11Z","finishedAt": "2026-04-30T15:42:34Z","durationSeconds": 142.91,"filtersApplied": { "minStars": 4.0, "websiteFilter": "withWebsite", … }}
Built-in dataset views
The actor ships three pre-built dataset views accessible from the Apify Console:
- Overview — title, category, address, contacts, rating, URL
- Lead generation — phone, website, owner, claim status, rating
- Hotels — stars, price, check-in/out, amenities
Use cases
| Use case | Why this scraper wins |
|---|---|
| Lead generation | 84%+ phone & website coverage, unclaimed-business detection (claimThisBusinessUrl), owner names, built-in withoutWebsite filter |
| Competitor monitoring | Quad-tree scans entire metro areas; structured addressParts makes geo-grouping trivial |
| Market analysis | Full additionalInfo amenities tree + placeTags — segment by accessibility, payment options, ownership demographics |
| Hotel/travel platforms | Hotel-specific block: stars, price, check-in/out dates, amenities, full description |
| Local SEO | Owner names, claim status, kgmid (Knowledge Graph IDs), canonical URLs |
| POI database building | placeId + fid + cid + kgmid = stable cross-reference identifiers |
| Multilingual datasets | additionalLanguages re-runs same areas in multiple languages |
Tips & tricks
Maximum coverage
{"searchStringsArray": ["restaurant", "cafe", "bar", "bakery"],"locationQuery": "Manhattan, New York","maxCrawledPlacesPerSearch": 5000,"maxSubdivisionDepth": 5,"multiZoomDelta": 1,"additionalLanguages": ["es", "zh"],"concurrency": 12}
Returns 2 000–5 000 unique restaurants across Manhattan — far beyond Google's per-area limit.
Maximum speed (small areas)
{"searchStringsArray": ["coffee"],"locationQuery": "Times Square","maxCrawledPlacesPerSearch": 50,"enableSubdivision": false,"concurrency": 4}
~40–50 places in under 5 seconds.
High-quality leads only
{"searchStringsArray": ["dentist"],"locationQuery": "Chicago, IL","placeMinimumStars": "fourAndHalf","websiteFilter": "withoutWebsite","skipClosedPlaces": true}
Targets unclaimed/no-website dentists with ≥4.5★ ratings — perfect for cold outreach.
Several non-contiguous areas in one run
{"customGeolocation": {"type": "MultiPolygon","coordinates": [[[[-74.05, 40.70], [-73.90, 40.70], [-73.90, 40.85], [-74.05, 40.85], [-74.05, 40.70]]],[[[-73.97, 40.55], [-73.85, 40.55], [-73.85, 40.62], [-73.97, 40.62], [-73.97, 40.55]]]]}}
Use multiple search terms instead of categories
Google's categoryName filter is brittle — a place tagged only "Coffee shop" won't match a "Cafe" query. Pass several related terms:
"searchStringsArray": ["coffee", "cafe", "espresso bar", "coffee roaster"]
Dedup is automatic via placeId.
Pricing
This Actor uses Apify's pay-per-event pricing — you're charged for compute units consumed during the run, not per place. Because there's no browser, runs are dramatically cheaper than browser-based alternatives.
Indicative cost (Apify residential proxy + 1 GB worker):
| Run profile | Places | Wall-clock | Approx. cost |
|---|---|---|---|
| Small neighborhood (50 places, depth=2) | 50 | ~10 s | ~$0.001 |
| Whole district (500 places, depth=4) | 500 | ~2 min | ~$0.01 |
| Whole city, multi-zoom (5 000 places) | 5 000 | ~15 min | ~$0.10 |
Exact cost depends on your Apify subscription tier and proxy traffic — see the Pricing tab on the Actor page for your live rate.
Integrations
This Actor works with Apify's full integration ecosystem:
- Apify integrations — Slack, Gmail, Google Sheets, Drive, Airtable, Make, Zapier, n8n
- Webhooks — fire on
ACTOR_RUN_SUCCEEDED,ACTOR_RUN_FAILED,ACTOR_RUN_RESURRECTED - Apify Schedules — run on cron (e.g. weekly competitor sweep)
- Apify Tasks — save common input presets and re-run with one click
- MCP server — drive this Actor from Claude / GPT / any LLM via the Apify MCP server
- Apify CLI —
apify call …for local testing & CI/CD
Output formats supported by Apify Datasets:
- JSON / JSON Lines
- CSV / Excel
- HTML / RSS
- Direct API access (paginated)
Honest limitations
We believe in being upfront. Here's what this scraper does not extract, and why:
| Field | Why not |
|---|---|
| Review text & individual reviews | Google's /maps/preview/review/listentitiesreviews requires browser-bound session tokens — impossible HTTP-only |
| Full week opening hours | Only "today" is in the search XHR |
| Popular times histogram | Browser-only XHR |
| Photo URLs (per-photo list) | Browser-only XHR — only imagesCount and a thumbnail URL are HTTP-accessible |
| Q&A | Browser-only XHR |
reviewsCount for non-hotels | Google has stripped this from the search XHR for restaurants/retail/etc. (we still extract it for hotels) |
If any of the above are critical for your use case, you'll need a browser-based scraper (which will be 5–10× slower and more expensive). For the 95% of users who need leads, addresses, contacts, ratings, and place identifiers — this scraper is the optimal choice.
FAQ
How does this compare to the official Google Places API?
Google's API is rate-limited, expensive at scale, capped at 60 results per query, and missing many fields. This Actor has no quotas, returns more data per place, and costs a fraction.
Will I get blocked?
Not if you use Apify residential proxies (the default). Chrome TLS impersonation rotates per request, sticky sessions per viewport keep IPs anchored, and intelligent backoff handles 429/503. We tested 1 000+ requests with zero captcha hits.
How many places can I get per area?
With enableSubdivision=true and maxSubdivisionDepth=4, you can scrape thousands of places per city. Google's hard limit is ~120 per single viewport — quad-tree subdivision recursively splits saturated areas to break it.
Does this extract reviews?
No. See Honest limitations.
Which fields are 100 % reliable?
title, categories, structured address (street/city/state/postal/country), coordinates, placeId, fid, cid, kgmid, totalScore, url, timezone, ownerName, imagesCount, language, rank, scrapedAt. Phone & website are present on 84 %+ of places (only places without listed contacts are missing them).
Can I resume an interrupted run?
Yes — state is checkpointed every 30 s and on Apify's PERSIST_STATE event. Migrated runs auto-resume without re-pushing duplicates.
When should I use multi-language passes?
When scraping non-English areas (Tokyo, Mexico City, Berlin, Montreal). Each language adds ~1× search budget but typically catches 5–15 % more unique places.
What if my location returns no results?
Verify the location name in OpenStreetMap Nominatim first. If Nominatim can't find it, use customGeolocation with explicit GeoJSON coordinates.
Can I scrape from a list of place URLs without searching?
Yes — pass them in startUrls (URLs) or placeIds (raw IDs). Each is fetched directly without going through search.
Tech stack
curl_cffi— TLS-impersonation HTTP client (Chrome fingerprint rotation)apifySDK ≥ 3.3.0 — Actor runtime, dataset, KV store, proxycrawlee≥ 1.5.0 — used as a browserforge bug workaround dependency- Python 3.12 — slim Apify base image, no system Chromium installed
No Node.js. No browser. No headless detection risk. Just Python + curl.
Changelog
v0.8 (latest)
- Add Key-value store schema for
OUTPUTandGMAPS_SCRAPER_STATErecords - Add Actor Output schema with templated download links
v0.7
- Run summary written to
OUTPUTkey (totalPlaces,durationSeconds,filtersApplied, …)
v0.6
- 13 new output fields:
scrapedAt,language,rank,searchPageUrl,permanentlyClosed,temporarilyClosed,isAdvertisement,price,menu,plusCode,locatedIn,inputPlaceId,inputStartUrl - 5 new post-fetch filters:
placeMinimumStars,websiteFilter,skipClosedPlaces,searchMatching,categoryFilterWords - Geo composite:
countryCode+state+county+city+postalCode - Direct
placeIdsinput - Honest "not retryable" handling for deterministic 4xx (no retry storms)
v0.5
- Output dataset schema (49 fields × 3 views: Overview / Leads / Hotels)
v0.4
- Comprehensive parser rewrite — 30+ fields per place
- AsyncSession reuse across pagination chain (single TLS handshake per task)
- Sticky proxy session per viewport
v0.3
- Quad-tree viewport subdivision
- Multi-language & multi-zoom expansion
- State persistence + migration resumability
- Worker pool + sticky session ID
v0.2
- Two-phase SSR → XHR flow with pagination
- Connection-reusing pipeline
v0.1
- Initial release
Support & feedback
- 🐛 Bug reports & feature requests — open an issue on the Issues tab of the Actor
- 💬 Questions — comment under the Actor or contact via Apify support
- ⭐️ Liked it? — leave a review on the Apify Store
If this scraper saves you compute units, please consider giving it a star — it helps other users find it.