Fast Google Maps Scraper
Pricing
from $0.20 / 1,000 place listeds
Fast Google Maps Scraper
HTTP-first Google Maps scraper for search listings, place details, reviews, lead scoring, and optional website enrichment.
Pricing
from $0.20 / 1,000 place listeds
Rating
0.0
(0)
Developer
Solutions Smart
Maintained by CommunityActor stats
1
Bookmarked
12
Total users
2
Monthly active users
2 days ago
Last modified
Categories
Features
Share
A lightweight Google Maps lead scraper built for low-cost prospecting. It uses a fast HTTP-first pipeline, optional deep details, and built-in opportunity scoring so you do not spend browser compute on every lead.
What it does
listmode searches Google Maps and saves lightweight listing rows only. This is the cheapest mode.placemode scrapes one known Google MapsplaceId.searchmode searches, fetches full place details, and optionally fetches reviews.outputModecontrols whether the Dataset contains all records, only places and reviews, or places only.- Supports concurrent place detail and review scraping in
searchmode. - Pushes every result immediately to the Apify Dataset.
- Saves a run summary to Key-Value Store record
OUTPUT. - Supports Apify Proxy and a manual proxy URL fallback.
- Adds lead scoring for every full place record.
- Finds emails and social links by visiting each business website (on by default). Google Maps does not publish emails, so this is the only source. Records get top-level
email(best address, own domain first),emails, andsocialLinksfields. - Supports pay-per-event monetization with charged Dataset writes for
place-listed,place-detail, andreview.
Performance and Memory
Concurrency: The maxConcurrency input (default 3, max 100) controls how many places are scraped in parallel during search mode. Higher concurrency is faster but requires more memory.
Memory allocation: Memory is configured in the Apify Console when starting a run, not in the input form:
- Minimum: 256 MB (suitable for list mode or low concurrency)
- Recommended: 1024 MB (default, good for concurrency 3-10)
- High concurrency: 2048-4096 MB (for concurrency >20)
The Actor will use more memory as concurrency increases. If you see out-of-memory errors, reduce maxConcurrency or increase the memory allocation.
Input
The Actor uses .actor/input_schema.json, so configuration is available as a typed Apify Console form.
Important fields:
mode:list,place, orsearch.outputMode:allRecords,placesAndReviews, orplacesOnly.query: business/category query forlistandsearch, for examplecoffee shops. Used whensearchTermsis empty.searchTerms: several queries in one run, for example["restaurant", "cafe", "bar"]. Each runs separately (case-insensitive duplicates are dropped) and places are deduplicated across terms.gridSearch: on by default. When one search returns fewer places thanmaxPlaces, the area is split into map tiles that are searched nearest-to-center first untilmaxPlacesis reached.gridCellKm: tile width, default2.searchLocation: free-form location, for exampleBerlin, Germany.geolocationParameters: grouped country, state/region, county, city, and postal code fields used whensearchLocationis empty.customGeolocation: grouped geometry type, coordinates, radius, latitude, longitude, and coordinates JSON fields for anchoring the map area.leadFilters: optional minimum rating, minimum review count, website, phone, and category filters.websiteEnrichmentDepth:contactPages(default) orhomepageOnly.websiteTimeoutSeconds: separate short timeout for website enrichment requests.skipSocialWebsites: skips enrichment of social profile URLs by default to reduce slow tails and 429 responses.placeId: required forplace.maxPlaces: maximum places per search term (up to 10000).maxReviews: reviews per place. Use0to skip reviews.maxTotalReviews: global review cap across the run. Use0for no global cap.maxConcurrency: number of places to scrape in parallel insearchmode.language: Googlehlparameter.gl: Google country parameter.proxyConfiguration: Apify Proxy settings.delaySeconds: randomized delay between requests.enableSqliteCheckpoint: optional internal SQLite copy for deduplication/checkpointing.enrichWebsites: website enrichment for emails and social links. On by default.enableBillingEvents: legacy testing flag. Production monetization is detected from Apify pay-per-event pricing.
Output
Dataset records are streamed as they are found:
recordType: "listing"for search results.recordType: "place"for full place detail records with lead scoring.recordType: "review"for individual reviews.
Output modes:
allRecords: saves listings, places, and reviews.placesAndReviews: saves full places and reviews, but skips listing rows.placesOnly: saves only full place records and skips review fetching.
The OUTPUT key-value store record contains:
- mode and query
- start and finish timestamps
- places listed
- places scraped
- reviews scraped
- duplicates skipped
- failed places
- website enrichments
- skipped website enrichments
- places with at least one email (
placesWithEmail) status: "aborted"when the run was aborted (the record is still written)
Getting more than ~200 results
One Google Maps text search stops at roughly 120-220 places, however large the city. Google also ignores the map position when the query names a place, so "restaurant in Berlin" always returns the same central results.
With gridSearch on, the Actor first runs the normal text search. If that returns fewer than maxPlaces, it searches the bare term (for example restaurant) tile by tile across the area and keeps only new places inside it:
- The area is the location's boundary from OpenStreetMap (city, district, postcode or state). With
customGeolocation.radiusKmit is that circle's bounding box. With coordinates but no radius it is 5 km around the point. - Tiles are searched nearest to the center first, so a small
maxPlacesstill gets the central places. - Tiles are searched in parallel by
maxConcurrencyworkers. With Apify Proxy each worker has its own proxy session and switches to a fresh one when Google keeps blocking it; failed tiles are retried once by another worker. - A tile stops paging after two pages that add nothing new. The scan ends early after 40 tiles in a row find nothing new. Very large areas use bigger tiles automatically (about 400 tiles at most).
- With
customGeolocationcoordinates, the location text is not added to the search, so results stay around those coordinates. - With pay-per-event pricing, a term never searches for more places than the remaining budget can pay for.
- The
OUTPUTrecord lists, per term, how many places came from the text search and how many from the grid.
Lead scoring
Each full place record includes:
{"leadScore": {"score": 80,"tier": "hot","reasons": ["Has phone", "Has website", "High rating"]}}
Signals include phone, website presence or absence, rating, review volume, opening hours, photos, booking links, and commercial local categories.
Ready-made tasks
tasks/ holds seven saved configurations (Apify tasks) for common jobs:
| Task | What it runs |
|---|---|
smoke-test | 20 cafes in Berlin, to check a new build works |
berlin-restaurant-leads | 500 Berlin restaurants that have a website, with emails |
munich-trades-multi-term | Electricians, plumbers and roofers in Munich, 300 each |
frankfurt-top-dentists | Dentists rated 4.5+ with 50+ reviews and a phone number |
hamburg-cafes-list-only | Up to 1,000 Hamburg cafes, listings only (fast, cheap) |
cologne-gyms-homepage-emails | 300 gyms, emails from homepages only (faster enrichment) |
vienna-hotels-large | 2,000 Vienna hotels, 2 GB memory, concurrency 20 |
To create them in your Apify account, or update them after editing the JSON files, run:
$APIFY_TOKEN=<your token> python tasks/sync_tasks.py
The script uses the Actor <your-username>~fast-google-map-scraper by default. Pass a different Actor ID as the first argument if yours has another name. python test_tasks.py checks every task input against the input schema.
Example input
{"mode": "search","outputMode": "placesAndReviews","query": "dentists","searchLocation": "Austin, United States","customGeolocation": {"latitude": 30.2672,"longitude": -97.7431},"maxPlaces": 25,"maxReviews": 20,"maxTotalReviews": 200,"maxConcurrency": 3,"language": "en","gl": "us","delaySeconds": 1.5,"enrichWebsites": true,"websiteEnrichmentDepth": "contactPages","websiteTimeoutSeconds": 8,"skipSocialWebsites": true,"leadFilters": {"minRating": 4.0,"mustHaveWebsite": true},"proxyConfiguration": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"],"apifyProxyCountry": "US"}}
Cost notes
Use list mode when you only need names, ratings, categories, addresses, coordinates, and place IDs. It avoids detail and review endpoints.
Use search mode when you need complete lead records. Set maxReviews to 0 if reviews are not needed.
Use placesOnly when you want a lead list without review rows. It skips review fetching even if maxReviews is greater than 0.
The default prefilled input is intentionally small: 5 places, placesOnly, and no reviews. This keeps Apify Store automated QA runs fast and gives new users a low-cost first test. Increase maxPlaces, switch output mode, and enable reviews for production runs.
maxReviews is a per-place limit. For example, maxPlaces: 20 and maxReviews: 50 can produce up to 1,000 reviews. Use maxTotalReviews when you want a global cap.
Increase maxConcurrency to process multiple places in parallel. Start with 2 or 3 when using proxies. Higher values can reduce runtime, but they also increase request pressure and blocking risk.
Website enrichment is on by default because it is the only way to get emails. Turn it off when you only need Maps data and want the fastest, cheapest run.
websiteEnrichmentDepth: "contactPages" (default) fetches the homepage and, if it has no email on the business's own domain, up to 4 same-site Impressum, contact, and about pages (falling back to /impressum, /kontakt, /contact). It stops as soon as an own-domain email is found, and each site gets a 25-second total budget. homepageOnly is faster but misses most emails, which usually sit on the contact or Impressum page. Extraction handles mailto: links, HTML-entity encoding, Cloudflare email protection, and [at]/(dot) obfuscation, and drops placeholder and tracking addresses such as email@example.com or Sentry IDs.
websiteTimeoutSeconds is independent from the main Google Maps request timeout. Keep it low, such as 6 to 8, when you want predictable cost. skipSocialWebsites is enabled by default because social profile URLs rarely expose emails and often return 429 or slow responses through proxies.
Lead filters are applied after full place details are fetched:
{"leadFilters": {"minRating": 4.2,"minReviewCount": 25,"mustHaveWebsite": true,"mustHavePhone": false,"categoryIncludes": "cafe"}}
Filtered places are counted in the OUTPUT.filteredPlaces summary and skipped from Dataset output and review fetching.
Location targeting
You can target by city and country:
{"query": "coffee shops","searchLocation": "Berlin, Germany","gl": "de"}
Or use structured geolocation fields:
{"query": "coffee shops","geoCountry": "Germany","geolocationParameters": {"country": "Germany","city": "Berlin","postalCode": "10117"},"gl": "de"}
You can also add custom latitude and longitude to anchor the map area:
{"query": "coffee shops","searchLocation": "Berlin, Germany","customGeolocation": {"latitude": 52.520008,"longitude": 13.404954},"zoom": 13,"gl": "de"}
For coordinate-heavy workflows, use customCoordinates as simple text:
{"query": "coffee shops","searchLocation": "Berlin, Germany","customGeolocation": {"coordinates": "52.520008, 13.404954","radiusKm": 5}}
Or use coordinatesJson with a GeoJSON point:
{"query": "coffee shops","searchLocation": "Berlin, Germany","customGeolocation": {"coordinatesJson": "{\"type\":\"Point\",\"coordinates\":[13.404954,52.520008]}"}}