Google Maps Business Scraper — Country-Scale
Pricing
Pay per event
Google Maps Business Scraper — Country-Scale
Extracts every business Google Maps publishes inside a bounding box, by recursive geographic tiling. No login, no browser, no cookies.
Pricing
Pay per event
Rating
0.0
(0)
Developer
Oswaldo Carabano
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
a day ago
Last modified
Categories
Share
Extracts every business Google Maps publishes inside an area — not the first 200. No API key, no login, no cookies, no browser.
Google caps any single Maps query at about 200 results. So "all the restaurants in Miami" is not one big query: it is a tiling problem. This Actor splits the map into tiles, and splits again wherever a tile comes back full, until every tile returns under the cap. Then it tells you honestly whether it finished.
Quick start
- Pick a country and one or more search terms in that country's language (English in the US and Canada, Spanish elsewhere).
- Optionally give a bounding box to scrape a single city instead of a whole country.
- Set Maximum places if you want a hard cap on what the run costs.
{"country": "US","categories": ["restaurant"],"bbox": { "lat_min": 25.70, "lng_min": -80.32, "lat_max": 25.85, "lng_max": -80.13 },"maxPlaces": 1000}
That is Miami restaurants, capped at 1,000 businesses.
How it works — two passes
Pass 1 — census. One request per 1-degree cell. It classifies every cell as dense, border, foreign, exhausted or empty. This is the planning tool: Venezuela is 180 cells and was classified in 172 requests, about 20 seconds, showing that only 11.7% of the country needed any deeper work. Run it alone with Census only to learn what an area costs before spending anything.
Pass 2 — quadtree. Only the saturated cells get split into four and retried, level by level, until they come back under the threshold. Cells that turned out to belong to a neighbouring country are dropped instead of split — on Venezuela that alone saved about 682 wasted requests.
What you get
One row per business, deduplicated by feature_id, Google's own stable identifier, so
the same place never appears twice — not even across separate runs.
place_id is the same identifier the official Places API uses, so this dataset
joins to anything you already have from Google without extra work.
Fill rates — measured, and they depend on the market
Two independent measurements, both on 3 Sep 2026: 12,693 businesses across 10 regions and 12 verticals in a Latin American market, and 4,032 businesses across 8 cities in the US, Canada, Mexico, Colombia, Peru, Chile and Argentina.
| Field | Toronto | Miami | Mexico City | Remote regions |
|---|---|---|---|---|
name, address, latitude, longitude, place_id | 100% | 100% | 100% | 100% |
phone_e164 | 96.7% | 77.7% | 68.2% | 41.4% |
opening_hours | 96.1% | 74.3% | 81.9% | 48.9% |
rating | 97.5% | 77.0% | 92.0% | 55.5% |
website | 87.4% | 74.0% | 40.0% | 5.7% |
reviews_count | 0.4% | 11.6% | 0.0% | 0.3% |
Coverage is a property of the market, not of the tool. The same field runs from 96.7% in Toronto to 41.4% in a remote region. Those numbers are here so you can decide before you buy, not after.
By vertical, measured: dentists return a phone on 99.5% of rows and law firms on 100%; gyms on only 28.0%, because many publish a booking site instead.
What this Actor does not give you
- A review count you can rely on. Google serves it only in some responses:
reviews_countcame back on 8.5% of rows. It is delivered when present and it is never promised. If you need it on every row, this is not the tool. - Review text. Out of scope: it is writing by identifiable people.
- Email addresses. Google Maps does not publish them. Measured: zero email strings in 498 records across three verticals, and none on the place page either. Any tool promising emails is getting them from somewhere else.
- A postal code field. It is embedded inside
addressand its format varies by region; parsing it out would be guesswork, so it is left where Google put it. - Individual place pages. Phone, hours and website already come in the search response, so fetching each place would multiply the cost by about 200 for nothing.
Pricing — one price, no add-ons
Charged per business delivered, with a lower price on every Apify plan tier:
| Your plan | Per place |
|---|---|
| Free | $0.0010 |
| Bronze | $0.0008 |
| Silver | $0.0006 |
| Gold and above | $0.0005 |
Phone, opening hours, website, rating, categories and coordinates are included at that price. There are no add-on charges for applying a filter or for pulling place details — a habit in this category that can double the real cost per row.
Rows served from cache are charged the same and always declare their age through
from_cache and data_age_hours. Failed tiles are never charged, and the tiling
overhead — the many requests it takes to break the 200-result cap — is absorbed by the
Actor, not billed to you.
Reading the run summary
RUN_SUMMARY in the key-value store is part of the output, not telemetry:
| Field | What it means |
|---|---|
census_complete | false means pass 1 did not finish. What you got is the corner of the grid the sweep started from, not a census of the area |
tiles_unresolved | above zero means this census has gaps, and the log says so |
max_depth_reached vs max_depth_limit | equal means the census is truncated — raise the depth or split the area |
stop_reason | max_places, request_budget or timeout — which brake stopped the run |
places_from_cache | how many rows were served without touching Google |
A census with gaps is legitimate. A census with gaps that does not say so is not, and this Actor will always say so.
Large areas: use batches
A big country does not fit in one run — not because of cost but because of the run time
limit. Run pass 1 first, then run pass 2 in batches with a requestBudget and a bbox
per batch.
Batches are independent, resumable and free to retry: tiles already exhausted are skipped, so re-running a finished batch spends no requests at all. Measured: a second run of the same Venezuelan census needed 8 requests instead of 180.
Country scope
The Actor is global by construction — it takes a bounding box, not a country — but it ships with a catalogue of 24 countries and refuses to run on 8 of them: Spain, Portugal, France, Germany, Italy and the United Kingdom (GDPR / UK GDPR), Brazil (LGPD) and Cuba (regime not reviewed).
This is a deliberate limit enforced in code, not an oversight. Aggregating name, phone and coordinates for hundreds of thousands of small businesses produces a personal-data file, and those regimes require a legal basis this Actor does not document.
Whoever runs this Actor is the data controller for the output. Removal requests:
privacy@actorstack.dev.
Notes
Language changes the data, not the interface. The language comes from the country
and sets the language of categories and opening_hours, so two runs of the same area
in different languages produce different rows. The language used is in every row as
language.
Place names are kept exactly as Google publishes them, in their own alphabet and
with their own accents. Mérida is not Merida and Kraków is not Krakow.
Where it runs from. The Actor routes through a residential proxy. This is not an optimisation: Google returns HTTP 302 to Apify's own IP ranges, measured on 3 Sep 2026, so a direct run from the platform returns nothing at all.