Google Maps Scraper — Extract Business Data at Scale
Pricing
from $1.00 / 1,000 result scrapeds
Google Maps Scraper — Extract Business Data at Scale
Scrape Google Maps for business leads — extract name, address, phone, website, rating, reviews, hours, category, and coordinates. Residential proxies included. Pay per result. Export to CSV, JSON, or Excel.
Pricing
from $1.00 / 1,000 result scrapeds
Rating
0.0
(0)
Developer
Md Jakaria Mirza
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
Google Maps Scraper - Extract Business Data at Scale
Production-grade Apify Actor for scraping public Google Maps business listings worldwide. Enter any keyword and one or more global locations, then export clean business data to JSON, CSV, Excel, XML, or RSS from the Apify Dataset.
This Actor is built with Node.js 20, TypeScript, Apify SDK v3, Crawlee, and Playwright. It uses Apify residential proxies, session rotation, randomized delays, and resilient extraction logic for Google Maps result pages.
Apify Store Listing
Title: Google Maps Scraper - Extract Business Data at Scale
Short description: Scrape worldwide Google Maps listings with names, phones, websites, ratings, reviews, categories, hours, and coordinates. Export to CSV, JSON, or Excel.
Store description:
Google Maps Scraper - Extract Business Data at Scale helps teams collect structured local business data from Google Maps searches worldwide. Use it to find restaurants in London, dentists in Dubai, hotels in Tokyo, plumbers in Toronto, clinics in Berlin, or any other keyword/location combination. The Actor opens Google Maps in a real browser, scrolls the search feed, visits each business detail panel, and extracts practical B2B data including business name, address, phone, website, category, rating, reviews count, opening hours, coordinates, Google Maps ID, plus code, and Maps URL.
It supports batch scraping by combining multiple keywords with multiple locations in a single run. Locations can be cities, countries, postal codes, neighborhoods, addresses, latitude/longitude pairs, the built-in 169-city high_demand_markets preset, or the broader 171-city world_major_cities preset. Country targeting is optional: leave countryCode empty for worldwide location-text based searches, or set it to bias Google Maps and proxy country for a specific market.
The Actor is monetized with Apify Pay Per Event. Users pay only for rows successfully delivered to the dataset: result-scraped at $0.001 per result, or $1 per 1,000 results. Failed searches, empty locations, retries, and blocked pages do not create billable rows.
Built for lead generation, market research, local SEO audits, sales prospecting, and retail site selection, this scraper is designed for repeatable production use and Apify Store publishing.
What It Extracts
- Business name
- Address (full street address from the place page when
scrapeDetailsis enabled) - Phone number (from the place page when
scrapeDetailsis enabled) - Website
- Category
- Rating
- Reviews count
- Opening hours
- Latitude and longitude
- Google Maps ID
- Plus code (from the place page when
scrapeDetailsis enabled) - Google Maps URL
- Keyword, location, query, and scrape timestamp
Worldwide Use
This Actor is not limited to Kolkata, India, the United States, or any single country. It works globally because:
- Search locations are free-form strings, e.g.
Paris, France,Sao Paulo, Brazil,Shibuya, Tokyo, Japan,Dubai Marina, UAE. - The built-in
high_demand_marketspreset covers 169 commercial cities where lead-generation, agency, local SEO, sales, and enrichment use cases are most likely to convert into paid runs. - The built-in
world_major_citiespreset covers 171 big and famous cities across North America, Latin America, Europe, the Middle East, Africa, South Asia, East/Southeast Asia, and Oceania. countryCodeis optional and defaults to noglcountry bias.- The proxy country is not forced by default.
- If you set
countryCode, the Actor also uses that country for proxy targeting unless you explicitly overrideproxyConfig.apifyProxyCountry.
For best accuracy, include the country in each location. For example, use restaurants in Georgia, USA or restaurants Tbilisi, Georgia instead of only Georgia.
Preset Strategy
high_demand_markets: best default for monetization. Focuses on rich metro areas, agency-heavy cities, startup hubs, GCC markets, English-speaking countries, EU business centers, India metros, and fast-growing APAC/LATAM cities where customers commonly buy lead-generation, local SEO, market research, and enrichment data.world_major_cities: broader global coverage for users who want famous cities across more regions, including tourism, emerging markets, and general business hubs.none: use only custom locations when the customer already knows the exact target market.
Use Cases
- Lead generation: build prospect lists with phones, websites, addresses, and Maps URLs.
- Market research: compare competitor density, ratings, and categories by city or region.
- Local SEO audits: check business presence, category choices, and public NAP data.
- Sales territory planning: collect potential accounts before launching outreach in a new market.
- Retail and real estate analysis: identify dense commercial clusters and underserved areas.
Pricing
This Actor uses Apify Pay Per Event pricing.
| Event name | Price per event | 1,000 results | 10,000 results |
|---|---|---|---|
result-scraped | $0.001 | $1.00 | $10.00 |
The code charges only after a business record is extracted and before it is pushed to the dataset. If the charge budget is exhausted, the Actor stops gracefully. Dataset writes are retried after charging to reduce the risk of paid rows not being stored.
Input
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
keywords | array | yes | none | Business types or search terms. |
locations | array | no | none | Custom cities, regions, postal codes, addresses, or lat,lon pairs anywhere in the world. Required only when no preset is selected. |
locationPreset | string | no | none | Use high_demand_markets for 169 commercial hotspots, world_major_cities for broad global coverage, or none for custom locations only. Custom locations are added on top. |
maxResults | integer | no | 10 | Maximum saved rows per keyword/location pair. Start small for tests; increase for production. Allowed: 1-5000. |
maxCrawledPlacesPerQuery | integer | no | 20 | Maximum detail pages to inspect per query. Set higher than maxResults when many listings may be closed or duplicates. |
language | string | no | en | Result language code, e.g. en, de, es, fr, ja. |
countryCode | string | no | none | Optional 2-letter country bias, e.g. gb, de, jp, ae. |
skipClosedBusinesses | boolean | no | true | Skip listings marked permanently closed. |
scrapeDetails | boolean | no | true | Visit each place page when phone or full address is missing from the list view; also captures the plus code. Disable for faster list-only runs. |
proxyConfig | object | no | Residential Apify proxy | Apify proxy configuration. Leave country empty for global runs. |
The Actor removes duplicate keywords and locations. The keyword/location cartesian product is capped at 1,000 queries to prevent accidental huge runs. With either large preset, up to 5 keywords fit under the 1,000-query cap. For first tests, keep locationPreset as none and use 1-2 custom locations.
When scrapeDetails is enabled (default), the Actor performs an extra navigation to each place's individual Google Maps page whenever the list view did not expose a phone number or full address. Each detail page has a 10-second timeout. If the detail page fails, the Actor keeps the list-view data so no record is dropped.
Example Inputs
High-Demand Markets Batch
{"keywords": ["coffee shop"],"locationPreset": "high_demand_markets","maxResults": 25,"maxCrawledPlacesPerQuery": 50,"language": "en","skipClosedBusinesses": true,"scrapeDetails": true,"proxyConfig": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
Broad World Cities Batch
{"keywords": ["restaurant"],"locationPreset": "world_major_cities","maxResults": 25,"maxCrawledPlacesPerQuery": 50,"language": "en","skipClosedBusinesses": true,"scrapeDetails": true}
Custom Global Batch
{"keywords": ["coffee shop", "coworking space"],"locations": ["London, UK", "Berlin, Germany", "Tokyo, Japan", "Dubai, UAE"],"maxResults": 50,"maxCrawledPlacesPerQuery": 100,"language": "en","skipClosedBusinesses": true,"scrapeDetails": true,"proxyConfig": {"useApifyProxy": true,"apifyProxyGroups": ["RESIDENTIAL"]}}
Country-Biased Run
{"keywords": ["dentist"],"locations": ["Manchester", "Birmingham", "Leeds"],"maxResults": 200,"maxCrawledPlacesPerQuery": 300,"language": "en","countryCode": "gb","skipClosedBusinesses": true,"scrapeDetails": true}
Coordinate Search
{"keywords": ["hotel"],"locations": ["25.2048,55.2708", "48.8566,2.3522"],"maxResults": 100,"scrapeDetails": true}
Fast List-Only Run
{"keywords": ["pizza"],"locations": ["New York, USA"],"maxResults": 200,"scrapeDetails": false}
Sample Output
{"title": "Monmouth Coffee Company","address": "2 Park St, London SE1 9AB, United Kingdom","phone": "+44 20 7232 3010","website": "https://www.monmouthcoffee.co.uk/","category": "Coffee shop","rating": 4.5,"reviewsCount": 2584,"hours": ["Monday: 7:30 AM - 6:00 PM", "Tuesday: 7:30 AM - 6:00 PM"],"latitude": 51.5054,"longitude": -0.0911,"mapsId": "0x487604a9cddf0001:0x9876543210abcdef","plusCode": "7FJW+XV London, United Kingdom","url": "https://www.google.com/maps/place/Monmouth+Coffee+Company/@51.5054,-0.0911,17z","keyword": "coffee shop","location": "London, UK","query": "coffee shop London, UK","scrapedAt": "2026-06-07T09:30:00.000Z"}
{"title": "The Barn Cafe","address": "Auguststrasse 58, 10119 Berlin, Germany","phone": "+49 30 12345678","website": "https://thebarn.de/","category": "Cafe","rating": 4.4,"reviewsCount": 1760,"hours": ["Monday: 8:00 AM - 6:00 PM", "Tuesday: 8:00 AM - 6:00 PM"],"latitude": 52.5281,"longitude": 13.4022,"mapsId": "0x47a851eb00000001:0xabcdef1234567890","plusCode": "4F4V+82 Berlin, Germany","url": "https://www.google.com/maps/place/The+Barn+Cafe/@52.5281,13.4022,17z","keyword": "coffee shop","location": "Berlin, Germany","query": "coffee shop Berlin, Germany","scrapedAt": "2026-06-07T09:31:00.000Z"}
{"title": "Streamer Coffee Company Shibuya","address": "1 Chome-20-28 Shibuya, Shibuya City, Tokyo 150-0002, Japan","phone": "+81 3-6427-3705","website": "https://streamer.coffee/","category": "Coffee shop","rating": 4.3,"reviewsCount": 2103,"hours": ["Monday: 8:00 AM - 7:00 PM", "Tuesday: 8:00 AM - 7:00 PM"],"latitude": 35.6614,"longitude": 139.7040,"mapsId": "0x60188ca900000001:0x1234567890abcdef","plusCode": "8Q7X+4F Shibuya City, Tokyo, Japan","url": "https://www.google.com/maps/place/Streamer+Coffee+Company+Shibuya/@35.6614,139.7040,17z","keyword": "coffee shop","location": "Tokyo, Japan","query": "coffee shop Tokyo, Japan","scrapedAt": "2026-06-07T09:32:00.000Z"}
API Example
curl -X POST "https://api.apify.com/v2/acts/YOUR_ACTOR_ID/runs?token=YOUR_API_TOKEN" \-H "Content-Type: application/json" \-d '{"keywords": ["restaurant", "dentist"],"locations": ["Dubai, UAE", "Toronto, Canada", "Singapore"],"maxResults": 150,"language": "en"}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: 'YOUR_API_TOKEN' });const run = await client.actor('YOUR_ACTOR_ID').start({keywords: ['restaurant', 'dentist'],locations: ['Dubai, UAE', 'Toronto, Canada', 'Singapore'],maxResults: 150,language: 'en',});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(`Got ${items.length} businesses`);
How It Works
- Validates input with clear errors.
- Builds one Google Maps search URL per keyword/location pair.
- Adds
hllanguage and optionalglcountry bias only when provided. - Runs Playwright through Apify residential proxies and Crawlee sessions.
- Scrolls Google Maps results until enough places are loaded or the feed ends.
- Opens each listing detail panel and extracts business data.
- If
scrapeDetailsis enabled and phone or address is still missing, visits the place's individual Google Maps page with a 10-second timeout to retrieve phone, full address, and plus code. - Deduplicates records by Maps ID and title.
- Charges
result-scrapedand writes records to the Apify Dataset.
Anti-Bot Handling
- Apify residential proxy support.
- Optional country-matched proxy targeting.
- Session pool with limited usage per session.
- Randomized scroll, click, and extraction delays.
- Retry support for transient blocks and navigation failures.
- Graceful field-level fallback to
nullwhen optional data is unavailable.
Project Structure
google-maps-scraper/.actor/actor.jsonDockerfilesrc/main.tsroutes.tstypes.tsINPUT_SCHEMA.jsoninput.jsonpackage.jsonpackage-lock.jsonREADME.mdtsconfig.json
Local Development
npm cinpm run buildnpx playwright install chromiumnpm start
For Apify local runs, put your input in storage/key_value_stores/default/INPUT.json or use the included input.json as a starting point.
Suggested Next Updates
- Add live smoke-test fixtures for 3 countries before each release.
- Add optional review scraping as a separate PPE event, e.g.
review-scraped. - Add an
includeEmailsFromWebsiteoption that visits business websites and extracts public emails. - Add region presets such as North America, Europe, GCC, APAC, and LATAM.
- Add a per-query summary dataset or key-value output for marketplace-quality reporting.
Known Limits
- With
scrapeDetails: true(default), keepmaxCrawledPlacesPerQueryat 200 or below. Larger values combined with detail-page enrichment can exceed the per-query request handler timeout of 3600 seconds and cause the query to be retried by Crawlee. - The per-run timeout is 12 hours (
timeoutSecs: 43200in.actor/actor.json). This supports up to ~70 queries with detail scraping enabled, ~100 queries for list-only runs (scrapeDetails: false) at typical speed (3-5 seconds per record without detail scraping, 5-8 seconds with detail scraping when phone or full address is missing). - Detail-page enrichment only fires when phone or full address is missing from the list view, so runs against well-structured queries where the list view already exposes the phone will not pay the extra navigation cost.
- Duplicate detection is based on
mapsId(or title whenmapsIdis unavailable). Two chain stores with the same business name but differentmapsIdare kept as separate records.
Legal And Ethical Use
Use this Actor for legitimate business intelligence, lead generation, local SEO, and market research. You are responsible for complying with Google Maps terms, privacy laws, anti-spam rules, GDPR, CAN-SPAM, and local outreach regulations in every country where you use the data.
License
Apache-2.0. See LICENSE.