Fast Google Maps Scraper avatar

Fast Google Maps Scraper

Pricing

from $0.20 / 1,000 place listeds

Go to Apify Store
Fast Google Maps Scraper

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

Solutions Smart

Maintained by Community

Actor stats

1

Bookmarked

12

Total users

2

Monthly active users

2 days ago

Last modified

Features

Limited permissions

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

  • list mode searches Google Maps and saves lightweight listing rows only. This is the cheapest mode.
  • place mode scrapes one known Google Maps placeId.
  • search mode searches, fetches full place details, and optionally fetches reviews.
  • outputMode controls whether the Dataset contains all records, only places and reviews, or places only.
  • Supports concurrent place detail and review scraping in search mode.
  • 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, and socialLinks fields.
  • Supports pay-per-event monetization with charged Dataset writes for place-listed, place-detail, and review.

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, or search.
  • outputMode: allRecords, placesAndReviews, or placesOnly.
  • query: business/category query for list and search, for example coffee shops. Used when searchTerms is 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 than maxPlaces, the area is split into map tiles that are searched nearest-to-center first until maxPlaces is reached.
  • gridCellKm: tile width, default 2.
  • searchLocation: free-form location, for example Berlin, Germany.
  • geolocationParameters: grouped country, state/region, county, city, and postal code fields used when searchLocation is 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) or homepageOnly.
  • 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 for place.
  • maxPlaces: maximum places per search term (up to 10000).
  • maxReviews: reviews per place. Use 0 to skip reviews.
  • maxTotalReviews: global review cap across the run. Use 0 for no global cap.
  • maxConcurrency: number of places to scrape in parallel in search mode.
  • language: Google hl parameter.
  • 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.radiusKm it 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 maxPlaces still gets the central places.
  • Tiles are searched in parallel by maxConcurrency workers. 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 customGeolocation coordinates, 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 OUTPUT record 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:

TaskWhat it runs
smoke-test20 cafes in Berlin, to check a new build works
berlin-restaurant-leads500 Berlin restaurants that have a website, with emails
munich-trades-multi-termElectricians, plumbers and roofers in Munich, 300 each
frankfurt-top-dentistsDentists rated 4.5+ with 50+ reviews and a phone number
hamburg-cafes-list-onlyUp to 1,000 Hamburg cafes, listings only (fast, cheap)
cologne-gyms-homepage-emails300 gyms, emails from homepages only (faster enrichment)
vienna-hotels-large2,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]}"
}
}
⌂ 换网址