Google Maps Scraper — Extract Business Data at Scale avatar

Google Maps Scraper — Extract Business Data at Scale

Pricing

from $1.00 / 1,000 result scrapeds

Go to Apify Store
Google Maps Scraper — Extract Business Data at Scale

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

Md Jakaria Mirza

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

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 scrapeDetails is enabled)
  • Phone number (from the place page when scrapeDetails is enabled)
  • Website
  • Category
  • Rating
  • Reviews count
  • Opening hours
  • Latitude and longitude
  • Google Maps ID
  • Plus code (from the place page when scrapeDetails is 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_markets preset 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_cities preset covers 171 big and famous cities across North America, Latin America, Europe, the Middle East, Africa, South Asia, East/Southeast Asia, and Oceania.
  • countryCode is optional and defaults to no gl country 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 override proxyConfig.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

  1. Lead generation: build prospect lists with phones, websites, addresses, and Maps URLs.
  2. Market research: compare competitor density, ratings, and categories by city or region.
  3. Local SEO audits: check business presence, category choices, and public NAP data.
  4. Sales territory planning: collect potential accounts before launching outreach in a new market.
  5. Retail and real estate analysis: identify dense commercial clusters and underserved areas.

Pricing

This Actor uses Apify Pay Per Event pricing.

Event namePrice per event1,000 results10,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

FieldTypeRequiredDefaultDescription
keywordsarrayyesnoneBusiness types or search terms.
locationsarraynononeCustom cities, regions, postal codes, addresses, or lat,lon pairs anywhere in the world. Required only when no preset is selected.
locationPresetstringnononeUse 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.
maxResultsintegerno10Maximum saved rows per keyword/location pair. Start small for tests; increase for production. Allowed: 1-5000.
maxCrawledPlacesPerQueryintegerno20Maximum detail pages to inspect per query. Set higher than maxResults when many listings may be closed or duplicates.
languagestringnoenResult language code, e.g. en, de, es, fr, ja.
countryCodestringnononeOptional 2-letter country bias, e.g. gb, de, jp, ae.
skipClosedBusinessesbooleannotrueSkip listings marked permanently closed.
scrapeDetailsbooleannotrueVisit 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.
proxyConfigobjectnoResidential Apify proxyApify 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
}
{
"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

  1. Validates input with clear errors.
  2. Builds one Google Maps search URL per keyword/location pair.
  3. Adds hl language and optional gl country bias only when provided.
  4. Runs Playwright through Apify residential proxies and Crawlee sessions.
  5. Scrolls Google Maps results until enough places are loaded or the feed ends.
  6. Opens each listing detail panel and extracts business data.
  7. If scrapeDetails is 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.
  8. Deduplicates records by Maps ID and title.
  9. Charges result-scraped and 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 null when optional data is unavailable.

Project Structure

google-maps-scraper/
.actor/
actor.json
Dockerfile
src/
main.ts
routes.ts
types.ts
INPUT_SCHEMA.json
input.json
package.json
package-lock.json
README.md
tsconfig.json

Local Development

npm ci
npm run build
npx playwright install chromium
npm 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 includeEmailsFromWebsite option 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), keep maxCrawledPlacesPerQuery at 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: 43200 in .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 when mapsId is unavailable). Two chain stores with the same business name but different mapsId are kept as separate records.

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.