Google Maps Scraper
Pricing
from $2.70 / 1,000 results
Google Maps Scraper
Extract business data from Google Maps at scale. Get names, addresses, phone numbers, websites, ratings, reviews, opening hours, popular times, photos, and 40+ data points per listing.
Pricing
from $2.70 / 1,000 results
Rating
0.0
(0)
Developer
Abot API
Maintained by CommunityActor stats
0
Bookmarked
91
Total users
2
Monthly active users
7 days ago
Last modified
Categories
Share
Google Maps Scraper: Business Listings, Reviews & Location Data
Google Maps Scraper turns Google Maps into structured business data. Search by keyword, paste Google Maps links, or draw a custom area, then pull names, addresses, phone numbers, websites, ratings, opening hours, popular times and reviews for every matching place. Export to JSON, CSV or Excel, or read the results straight through the API.
Why This Scraper?
- Two ways to find places. Free text search terms, or direct Google Maps place-page and search-result links, no manual URL building.
- Full business profiles. Contact details, location, category, price level, opening hours, popular times, photos and "people also search for" in one record.
- Reviews on demand. Turn on review extraction per run, with sort order, or skip it entirely for faster runs.
- Area coverage. Draw a custom polygon and the actor builds a grid of searches across it, so you are not limited to Google's per-search result cap.
- Built for schedules. Incremental mode returns only new and changed businesses on recurring runs, and a resumed run never re-scrapes a place it already collected.
Use Cases
- Lead generation: build prospect lists with names, phone numbers, websites and categories for a market or neighborhood.
- Local SEO and reputation tracking: monitor ratings, review counts and review text for your business and competitors over time.
- Market and site research: map competitor density, categories, price levels and ratings for real estate, retail or franchise planning.
- Data enrichment: attach verified addresses, coordinates and opening hours to an existing list of businesses.
- Delivery and menu research: collect menu links and order/delivery platform links for restaurants in an area.
Data You Get
Sample shape: values are illustrative placeholders, not from a live record.
| Field | Example |
|---|---|
placeId | "ChIJN1t_tDeuEmsRUsoyG83frY4" |
title | "Example Coffee Roasters" |
categoryName | "Coffee shop" |
address | "100 Example St, San Francisco, CA 94102, USA" |
street / city / state / postalCode / countryCode | Address components parsed from address, when Google's format allows it |
location | { "lat": 37.7763, "lng": -122.4158 } |
plusCode | "QHFJ+8R Hayes Valley, San Francisco, CA" |
phone | "+1 555 010 0100" |
website | "https://www.example.com" |
totalScore | 4.5 |
reviewsCount | 1842 |
priceLevel | "$$" |
openingHours | [{ "day": "Monday", "hours": "7 am to 6 pm" }, ...] |
popularTimes | Hourly occupancy percent per day, when Google shows it for that place |
imageUrls | Photo URLs, up to the configured limit |
peopleAlsoSearch | Related places Google shows on the same listing |
menuUrl / orderLinks | Menu link, and delivery platform links (Uber Eats, DoorDash, ...) when present |
locatedIn | Parent place, for a business inside a mall or airport |
temporarilyClosed / permanentlyClosed | Closure status flags |
searchString / rank / isAdvertisement | Which search found this place, its position, and whether it was a sponsored slot |
scrapedAt | "2025-12-06T10:30:00.000Z" |
reviews | Reviewer name, rating, text, date and owner response, when Max Reviews Per Business is above 0 |
changeType / changedFields / firstSeenAt / lastSeenAt | Present only when Incremental mode is on: how this business changed since the last tracked run |
How to Use
- Pick how to find places: free text search terms, or direct Google Maps links.
- Optionally narrow the area with a location string, coordinates, country/state/city/postal code, or a custom polygon, and set the map zoom.
- Set how many results, reviews and photos to collect per search, then click Start.
- Download the dataset as JSON, CSV or Excel, or read it through the API.
Basic keyword search:
{"searchStringsArray": ["coffee shops"],"location": "Seattle, WA","maxResultsPerSearch": 50}
Coordinate search with reviews:
{"searchStringsArray": ["dentists"],"lat": 41.8781,"lng": -87.6298,"zoom": 15,"maxReviews": 20,"reviewsSort": "newest"}
Custom area grid search:
{"searchStringsArray": ["hotels"],"customGeolocation": {"type": "Polygon","coordinates": [[[-122.5, 37.7], [-122.3, 37.7], [-122.3, 37.85], [-122.5, 37.85], [-122.5, 37.7]]]},"zoom": 16,"maxResultsPerSearch": 200}
Run it from your code
Python:
from apify_client import ApifyClientclient = ApifyClient("<YOUR_APIFY_TOKEN>")run = client.actor("abotapi/google-maps-scraper").call(run_input={"searchStringsArray": ["coffee shops"], "location": "Seattle, WA"})for business in client.dataset(run["defaultDatasetId"]).iterate_items():print(business["title"], business.get("totalScore"))
JavaScript:
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: '<YOUR_APIFY_TOKEN>' });const run = await client.actor('abotapi/google-maps-scraper').call({ searchStringsArray: ['coffee shops'], location: 'Seattle, WA' });const { items } = await client.dataset(run.defaultDatasetId).listItems();
Or connect it to Make, Zapier, n8n, Google Sheets or webhooks from the Integrations tab.
Grid search for large areas
Google Maps caps how many results a single search returns. Set customGeolocation to a GeoJSON Polygon or MultiPolygon and the actor generates a grid of search points across it based on zoom, so a large area is covered as many smaller searches instead of one capped one. A Point geometry is also accepted, but is treated as a single search location.
Resume and recurring updates
- Resume (
resumeFromRunId) continues one interrupted run: paste its run ID and the actor skips every Place ID it already collected there, so you don't pay twice. The deprecatedresumeRunIdfield still works the same way; if both are set,resumeFromRunIdwins. - Incremental mode (
incrementalMode) is for scheduled runs over the same search. Each business is classifiedNEW,UPDATED(withchangedFields),UNCHANGED(suppressed and not billed unlessemitUnchangedis on),REAPPEARED, orEXPIRED(only after a run that fully re-walked the search, and only withemitExpired).stateKeynames or shares the tracked baseline; leave it empty and the actor derives one from your search settings. With Incremental mode off, output is exactly as before.
Send results into your apps (MCP connectors)
Optionally pipe the scraped results into the apps you already use, via Model Context Protocol (MCP) connectors. This is an extra delivery step after the scrape, the Apify dataset is never changed.
What gets written to the connector: a condensed, human-readable summary of each business, not the full JSON. Each item becomes one entry with a title and its key fields flattened to plain text. The complete record always stays in the Apify dataset.
- Authorize a connector once under Apify → Settings → Integrations (Notion, Linear, Airtable, or Apify).
- Select it in the "Pipe results into your apps" input field. (If the picker is empty, you haven't authorized a connector yet.)
- For Notion, also set
notionParentPageUrlto the page where items should be created.
The connection is mediated by Apify's MCP proxy, so this actor never sees your third-party credentials. Leave the field empty to skip.
Input Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
searchStringsArray | array | ["restaurants"] | Free text search terms. |
startUrls | array | (none) | Direct Google Maps place-page or search-result links to scrape. |
location | string | "New York, NY" | Location text appended to each search term. |
lat | number | (none) | Latitude for a coordinate-based search (-90 to 90). Must be set together with lng. |
lng | number | (none) | Longitude for a coordinate-based search (-180 to 180). Must be set together with lat. |
country | string | (none) | Country used to build the search query when location is not set. |
state | string | (none) | State or region used to build the search query when location is not set. |
city | string | (none) | City used to build the search query when location is not set. |
postalCode | string | (none) | Postal or ZIP code filter. |
customGeolocation | object | (none) | GeoJSON Polygon or MultiPolygon boundary; the actor builds a grid of searches across it. A Point is accepted as a single search location. |
zoom | integer | 14 | Map zoom level (1 to 21). Higher values mean a smaller area per grid cell and more precise results. |
maxResultsPerSearch | integer | 2 | Maximum results to extract per search term or grid point (up to 10,000). |
maxReviews | integer | 0 | Maximum reviews to extract per business. 0 skips review extraction. |
maxImages | integer | 10 | Maximum photo URLs to extract per business. |
reviewsSort | string | relevant | Review sort order: relevant, newest, highest, or lowest. |
oneReviewPerRow | boolean | false | Output one dataset row per review instead of one row per business, useful for CSV export. |
skipClosedPlaces | boolean | false | Skip temporarily or permanently closed businesses. |
language | string | en | Language code for search results and extracted text. |
maxConcurrency | integer | 5 | Maximum number of pages processed in parallel (1 to 20). |
proxyConfiguration | object | Apify Proxy off | Connection settings for all requests. |
resumeFromRunId | string | (none) | Run ID of a previous run to continue; already-scraped Place IDs are skipped. |
resumeRunId | string | (none) | Deprecated, use resumeFromRunId instead. Still honored for existing saved tasks; resumeFromRunId wins if both are set. |
incrementalMode | boolean | false | Turn on recurring monitoring of the same search, classifying each business as NEW, UPDATED, UNCHANGED, REAPPEARED or EXPIRED across scheduled runs. |
stateKey | string | (none) | Manually name the Incremental mode baseline. Leave empty to derive it automatically from your search settings. |
emitUnchanged | boolean | false | Also output businesses with no changes since the last run (bills and returns an extra row for each). |
emitExpired | boolean | false | Also output a synthetic row for a previously tracked business no longer found, only after a run proves it fully re-scanned the search. |
mcpConnectors | array | (none) | Optional: send a summary of each business to apps you authorized under Integrations. |
notionParentPageUrl | string | (none) | Notion connector only: page under which item pages are created. |
maxNotifyListings | integer | 50 | Cap on items written to each connector per run. |
Output Example
Sample shape: values are illustrative placeholders, not from a live record.
{"placeId": "ChIJ49sQuK-AhYARM6U646SA12Y","title": "Example Coffee Roasters","url": "https://www.google.com/maps/place/Example-Coffee-Roasters/ChIJ49sQuK-AhYARM6U646SA12Y","categoryName": "Coffee shop","address": "100 Example St, San Francisco, CA 94102, USA","street": "100 Example St","city": "San Francisco","state": "CA","postalCode": "94102","countryCode": "US","location": { "lat": 37.7763, "lng": -122.4158 },"plusCode": "QHFJ+8R Hayes Valley, San Francisco, CA","phone": "+1 555 010 0100","website": "https://www.example.com","totalScore": 4.5,"reviewsCount": 1842,"priceLevel": "$$","openingHours": [{ "day": "Monday", "hours": "7 am to 6 pm" },{ "day": "Tuesday", "hours": "7 am to 6 pm" }],"imageUrls": ["https://lh3.googleusercontent.com/example=w800-h600"],"menuUrl": "https://www.example.com/menu","temporarilyClosed": false,"permanentlyClosed": false,"searchString": "coffee shops","rank": 3,"isAdvertisement": false,"scrapedAt": "2025-12-06T10:30:00.000Z","reviews": [{"reviewId": "review_0123456789abcdef","reviewerName": "Sample Reviewer","rating": 5,"text": "Great coffee and atmosphere.","publishedAtDate": "2025-11-22T09:00:00.000Z"}]}
Plan Requirement
The default connection works for most searches out of the box. For large grid searches across many polygon cells, or for high volumes of detail pages, switch Proxy Configuration to a residential group under Connection for steadier throughput. The Pricing tab shows the current rates.
FAQ
How much does it cost?
You pay per dataset row returned. Reviews are included inside each business row; if you turn on One Review Per Row, every review becomes its own row. The Pricing tab shows the current rates. Use Max Results Per Search to cap the cost of any run.
Is it legal to scrape Google Maps?
This actor collects only publicly available business listing data. You are responsible for how you use it: follow Google's terms and the laws that apply to you, and get legal advice if you plan commercial redistribution. Reviews and photos may carry their authors' own rights.
Can I get only new or changed businesses on a schedule?
Yes. Schedule the actor from the Schedules tab against the same search and turn on Incremental mode. Each run then returns only new, updated, reappeared and expired businesses, and unchanged ones are not billed unless you turn on Emit unchanged businesses.
Why do I only get 2 results for a search term?
maxResultsPerSearch defaults to 2. Raise it, up to 10,000, to collect more results per search term or grid point. For very large areas, a customGeolocation grid search covers more ground than a single search term ever can.
Why did my run finish with an empty or partial dataset instead of failing?
The actor only stops the whole run for a configuration problem: no searchStringsArray or startUrls provided, lat set without lng (or the reverse), coordinates out of range, or turning on Incremental mode with resumeFromRunId against a search that already has tracked state. If Google Maps blocks or fails to load an individual search or business page, that one page is skipped and logged, and the run still finishes and returns whatever it did collect, which can be an empty dataset if every page was blocked. Run it again, or lower maxConcurrency, if you see this often.
Can I use it with AI agents or MCP?
Yes. Call it from any Apify integration or MCP client, and use the connector field to push results into Notion, Linear or Airtable.
🔗 Want more leads data?
Pair this actor with these related scrapers from the same team:
| 📇 2GIS From $1/1K. Extract business and place data from 2GIS.com at scale. Get names, addresses... | 🧩 Yandex Maps Scraper Scrape businesses and places from Yandex Maps by search term or URL for research, leads... |
| ✈️ Naver Map Scraper From $1/1K. Scrape Naver Map places by keyword or URL: names, categories, ratings... | 📇 Herold.at Scraper From $0.80/1K. Scrape Herold.at business listings across Austria into clean JSON. Extract... |
| 📇 PagesJaunes FR From $0.8/1K. Scrapes business listings from PagesJaunes.fr (French Yellow Pages)... | 📇 BizBuySell Scraper From $1.5/1K. Extract business-for-sale and franchise listings from bizbuysell.com with... |
💬 Support
- 🐞 Found a bug or a missing field? Open a ticket on the Issues tab. We usually reply within hours.
- 🛠️ Need another site, extra fields or a private build? Email contact@abotapi.com.
- ⭐ Enjoying it? A quick review on the actor page helps other users find it.