Google Maps Scraper | $1/1k Places
Pricing
from $0.75 / 1,000 place scrapeds
Google Maps Scraper | $1/1k Places
The cheapest Google Maps scraper on Apify. Extract places, reviews & contacts for $1/1k results. Lightning fast, minimal resources. Optional email/phone extraction and AI-powered contact & job data from company websites.
Pricing
from $0.75 / 1,000 place scrapeds
Rating
5.0
(2)
Developer
NanoScrape
Maintained by CommunityActor stats
9
Bookmarked
461
Total users
145
Monthly active users
8 hours
Issues response
7 hours ago
Last modified
Categories
Share
Google Maps Scraper: businesses, reviews, popular times, contacts, jobs
Scrape Google Maps search results anywhere in the world. You get business names, addresses, phone numbers, websites, reviews, ratings, opening hours, GPS coordinates, and 30+ structured fields per place. Fast, reliable, and priced per result.
Optionally chain enrichment on every website found: emails and phones via a regex extractor (no API key), or AI-powered contacts and job listings using your own Gemini, Groq, or OpenRouter key. All merged into a single dataset row per business.
Why use this scraper
- All-inclusive pricing. 30+ fields per place at one flat rate. No extra charges for reviews, photos, hours, or address parsing.
- Fast. Results in seconds, not minutes.
- Volume-friendly. $1.00 per 1,000 places.
- Built-in enrichment. One input flag adds emails, phones, socials, contacts, or job listings from each business's website.
- Bring your own LLM key. Use Gemini (recommended, free tier available), Groq, or OpenRouter. Automatic fallback between providers when you supply multiple keys.
- Works worldwide. Any country, any language. Pass an ISO language code and get localized results.
Use with AI agents (MCP)
Connect this actor to any MCP-compatible AI client: Claude Desktop, Claude.ai, Cursor, VS Code, LangChain, LlamaIndex, or a custom agent.
MCP server URL:
https://mcp.apify.com?tools=santamaria-automations/google-maps-scraper
Example prompt once connected:
"Use
google-maps-scraperto find pediatric dentists in Zurich and put the results in a table."
Clients that support dynamic tool discovery (Claude.ai, VS Code) will receive the full input schema automatically via add-actor.
Quick start
Simple search
Pass plain search strings. Include the location in the string itself:
{"searchStrings": ["restaurants in Paris","coffee shops in Tokyo","dentists in New York"],"maxResultsPerQuery": 20}
Structured queries
Use queries when you want to attach your own company_id for join-back, or split the search term from the location:
{"queries": [{"query": "software companies","location": "Berlin","country": "DE","company_id": "my-internal-id-123"},{"query": "plumbers","location": "London","country": "GB"}],"maxResultsPerQuery": 50,"language": "en"}
Add emails, phones, and social links (no API key)
The email extractor pulls contact details straight from each place's website using regex. No LLM key needed, no AI cost.
{"searchStrings": ["altenpflegeheim konstanz"],"maxResultsPerQuery": 20,"enableEmailExtraction": true}
Each place row gets four new fields: emails_from_website, phones_from_website, social_urls, and address_from_website.
Add AI-powered contacts and job listings
Bring your own LLM API key. Structured contacts (name, position, email, phone) and job listings get merged into the same dataset row as the Maps data.
{"searchStrings": ["IT companies in Munich"],"maxResultsPerQuery": 20,"enableContactExtraction": true,"enableJobExtraction": true,"enableEmailExtraction": true,"groqApiKey": "gsk_your_own_key","waitForAddOns": true}
The scraper waits for each add-on to finish and merges the results into each place row. You end up with one row per business, everything in the same dataset. That's the flow that works nicely for n8n, Zapier, or Make workflows where you don't want to join multiple datasets.
Input parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
searchStrings | string[] | [] | Simple search strings. Include the location in the string for geo-targeting. |
queries | object[] | [] | Structured queries with optional query, location, country, company_id. |
maxResultsPerQuery | integer | 20 | Results per query. Max 120 (Google Maps page limit). |
maxTotalResults | integer | (none) | Optional global cap across all queries combined. Leave empty for no cap. Useful for budget control. |
maxResults | integer | (none) | Deprecated alias for maxResultsPerQuery. Still works for backward compatibility. |
language | string | "en" | ISO 639-1 language code (en, de, fr, ja, es, pt, it, ko, zh, ar, etc.). |
includeReviewCount | boolean | true | Enrich each result with review_count via the Maps Embed API. Adds about 150 ms per place. |
includePopularTimes | boolean | false | Opt-in. Enrich each result with the visitor-traffic histogram by weekday × hour (0–100). Adds about 1.5 s per place. |
includeReviews | boolean | false | Opt-in. Fetch featured reviews for each place (author, rating, date, text). Adds about 300 ms per place. |
maxReviewsPerPlace | integer | 25 | Maximum reviews per place. Set to 0 for no cap. |
reviewsSort | string | most_relevant | Sort order: most_relevant, newest, highest_rating, lowest_rating. |
reviewsNewerThan | string | (none) | Only return reviews on or after this date (YYYY-MM-DD). Auto-switches sort to newest for an efficient cutoff. |
enableEmailExtraction | boolean | false | Regex extractor for emails, phone numbers, and social URLs (LinkedIn, Facebook, Instagram, Twitter, YouTube). No LLM key required. |
enableContactExtraction | boolean | false | AI-powered structured contact extraction (name, position, email, phone). Requires your own LLM API key. |
enableJobExtraction | boolean | false | AI-powered job-listing extraction from careers pages. Requires your own LLM API key. |
enableBrowserFallback | boolean | false | Re-scrape JS-rendered sites (React, Vue, Angular) with a full browser. Catches about 25% more sites. |
outputLanguage | string | "en" | Language for AI-extracted fields (en, de, fr, it, es, pt, nl, auto). |
geminiApiKey | string | (none) | Your Gemini API key. Free tier is generous, so this is the recommended starting point. |
groqApiKey | string | (none) | Your Groq API key. Ultra-fast inference with a free tier. |
openrouterApiKey | string | (none) | Your OpenRouter API key. Access to 100+ models. Optional fallback. |
llmModel | string | (provider default) | Optional model override. Useful when your OpenRouter key doesn't have access to the default (google/gemini-2.0-flash-001) and you need to point at a free model like meta-llama/llama-3.1-8b-instruct. Also works for Gemini and Groq. |
waitForAddOns | boolean | true | When true (default): the scraper waits for each add-on and merges its output into the same dataset row as the Maps result. When false: add-ons run fire-and-forget and write to their own datasets, and you handle the join yourself. |
addOnWaitCapMs | integer | 1800000 | Safety cap on how long to wait for add-ons before flushing remaining places with a timed_out status. Default 30 min. |
excludeCids | string[] | [] | Google CIDs to skip. Useful for excluding already-scraped places. |
maxConcurrency | integer | 10 | Parallel requests (1–20). |
requestDelay | integer | 300 | Delay between pagination requests in milliseconds (100–10,000). |
proxyConfiguration | object | Apify proxy | Proxy settings. Datacenter proxies work well and are cost-efficient. |
Use either searchStrings or queries, or both. searchStrings sends each string directly to Google Maps. queries lets you separate the search term from the location and attach your own company_id for linking results back to your data.
About the LLM API key fields. They're shown as plain text (not masked) in the Apify UI. That's a technical necessity for how Apify forwards fields to sub-actors right now: secret-marked fields get encrypted on the parent side and the sub-actor can't decrypt them. If the visibility bothers you, use a scoped or throwaway key.
Output
Each result has 30+ fields. Here's a shortened example (real Paris restaurant, some URLs abbreviated):
{"company_id": "restaurants in Paris","title": "Le Petit Cler","category": "French restaurant","categories": ["French restaurant", "Restaurant"],"address": "3 Rue Cler, 75007 Paris, France","complete_address": {"street": "3 Rue Cler","city": "Paris","postal_code": "75007","state": "Ile-de-France","country": "France"},"latitude": 48.8571,"longitude": 2.3007,"plus_code": "8FW4V87C+X3","timezone": "Europe/Paris","phone": "+33 1 45 51 24 52","website": "https://www.lepetitcler.com/","emails": [],"review_rating": 4.5,"review_count": 1823,"reviews_per_rating": { "1": 42, "2": 31, "3": 89, "4": 312, "5": 1349 },"reviews_link": "https://search.google.com/local/reviews?placeid=...","user_reviews": [{"name": "Marie Dupont","profile_link": "https://www.google.com/maps/contrib/...","rating": 5,"text": "Excellent cuisine, warm atmosphere...","published_at": "2025-12-10","response_from_owner": null}],"status": "OPERATIONAL","price_range": "$$","open_hours": {"Monday": ["12:00-14:30", "19:00-22:30"],"Tuesday": ["12:00-14:30", "19:00-22:30"],"Wednesday": ["12:00-14:30", "19:00-22:30"],"Thursday": ["12:00-14:30", "19:00-22:30"],"Friday": ["12:00-14:30", "19:00-23:00"],"Saturday": ["12:00-23:00"],"Sunday": []},"thumbnail": "https://lh3.googleusercontent.com/places/...","images": [{ "title": "Interior", "image": "https://lh3.googleusercontent.com/..." }],"owner": { "name": "Le Petit Cler", "link": "https://maps.google.com/..." },"about": [{ "id": "dine_in", "name": "Dine-in" },{ "id": "reservations", "name": "Reservations" }],"reservations": [{ "link": "https://www.thefork.com/...", "source": "TheFork" }],"order_online": [],"menu": { "link": "https://www.lepetitcler.com/menu", "source": "lepetitcler.com" },"description": "Cozy French bistro in the heart of the 7th arrondissement.","link": "https://www.google.com/maps/place/Le+Petit+Cler/...","cid": "12345678901234567","data_id": "0x47e671a4e...","searchString": "restaurants in Paris","_rank": 1,"scraped_at": "2026-03-05T10:30:00.000Z"}
When you enable add-ons with waitForAddOns: true, the same row gets these extra fields merged in:
{"emails_from_website": ["info@lepetitcler.com", "reservations@lepetitcler.com"],"phones_from_website": ["+33 1 45 51 24 52"],"social_urls": {"facebook": "https://www.facebook.com/lepetitcler","instagram": "https://www.instagram.com/lepetitcler"},"address_from_website": "3 Rue Cler, 75007 Paris","contacts": [{ "name": "Marie Dubois", "position": "Head Chef", "email": "marie@lepetitcler.com" }],"job_listings": [{ "title": "Sous Chef", "location": "Paris", "job_url": "https://...", "ats_system": "personio" }],"_enrichment_status": { "emails": "success", "contacts": "success", "jobs": "no_data" }}
Complete field reference
| Field | Type | Description |
|---|---|---|
company_id | string | Your reference ID (from queries) or the search string |
title | string | Business name |
category | string | Primary Google Maps category |
categories | string[] | All assigned categories |
address | string | Full formatted address |
complete_address | object | Parsed components: street, city, postal_code, state, country |
latitude | number | GPS latitude |
longitude | number | GPS longitude |
plus_code | string | Google Plus Code |
timezone | string | IANA timezone identifier |
phone | string | Phone number |
website | string | Website URL |
emails | string[] | Email addresses found on the listing |
open_hours | object | Opening hours grouped by day of week |
popular_times | object | Visitor traffic patterns (opt-in via includePopularTimes) |
price_range | string | Price level ($ to $$$$) |
status | string | OPERATIONAL, CLOSED_TEMPORARILY, or CLOSED_PERMANENTLY |
review_count | integer | Total number of reviews |
review_rating | number | Average rating (1.0 to 5.0) |
reviews_per_rating | object | Breakdown of reviews by star rating (1 through 5) |
reviews_link | string | Direct URL to all reviews |
user_reviews | array | Sample reviews with name, rating, text, date, owner response |
thumbnail | string | Main business photo URL |
images | array | Additional photos with titles |
reservations | array | Reservation links with source (TheFork, OpenTable, etc.) |
order_online | array | Online ordering links with source |
menu | object | Menu link and source |
owner | object | Business owner name and profile link |
about | array | Amenities and features (dine-in, Wi-Fi, wheelchair access, etc.) |
description | string | Business description from Google |
link | string | Full Google Maps URL for this place |
cid | string | Google Maps CID: a stable identifier that persists across name and address changes |
data_id | string | Google internal data ID |
searchString | string | The search query that produced this result |
_rank | integer | Position in search results (1-based) |
scraped_at | string | ISO 8601 timestamp |
emails_from_website | string[] | Emails extracted from the company's website. Add-on: email extractor |
phones_from_website | string[] | Phone numbers extracted from the company's website |
social_urls | object | Social profiles from the website: linkedin, facebook, instagram, twitter, youtube |
address_from_website | string | Postal address from schema.org data on the website |
contacts | array | AI-extracted structured contacts from the website (name, position, email, phone). Add-on: contact extractor |
job_listings | array | AI-extracted job listings from the careers page. Add-on: job extractor |
_enrichment_status | object | Per-add-on status: success, no_data, failed, timed_out, or skipped. Present when any add-on is enabled and waitForAddOns is true |
About popular_times
Set includePopularTimes: true to enrich each result with Google's visitor-traffic histogram. The output shape is Day → Hour → Popularity(0-100) with English day keys (Sunday, Monday, ...) regardless of the input language, so the data stays portable across runs.
Each enrichment is one extra request to Google's place-detail endpoint, run sequentially (concurrency 1) with a 300 ms delay. Budget roughly 1.5 s per place on top of the SERP scrape. Only places with enough foot traffic for Google to compute the histogram will have non-null data. Smaller venues keep popular_times: null, which is expected, not an error.
Example:
"popular_times": {"Sunday": {"12": 41, "13": 56, "14": 63, "15": 70, "16": 73, "17": 83, "18": 76},"Monday": {"11": 43, "12": 61, "13": 60, "14": 53}}
About user_reviews
Set includeReviews: true to enrich each result with the place's featured reviews. Each review has the author's name, star rating, publication date, profile link, and text when the reviewer wrote one. Adds one extra request per place (about 300 ms).
maxReviewsPerPlacecaps the number of reviews per place (default 25). Set to0for no cap. The actual number returned depends on how many featured reviews Google surfaces, typically up to 8 for high-traffic places.reviewsSortcontrols ordering:most_relevant(default),newest,highest_rating,lowest_rating.reviewsNewerThan(YYYY-MM-DD) filters to reviews published on or after that date. It automatically switches sort tonewestso scraping stops as soon as older reviews are reached.
Reviews come from Google's place-preview endpoint, which returns the same featured reviews shown in the Google Maps overview panel. These are the reviews Google has selected as most helpful, typically the most recent positive reviews with photos or detailed text. Only places with enough review activity will return non-empty user_reviews; low-traffic places get user_reviews: [].
"user_reviews": [{"name": "Marie Dupont","profile_link": "https://www.google.com/maps/contrib/114798109881286611060","rating": 5,"text": "Excellent cuisine, warm atmosphere.","published_at": "2025-12-10","response_from_owner": null}]
Enrichment add-ons
After the Maps scrape finishes, this actor can trigger enrichment sub-actors on every website it found and merge everything back into a single dataset. One row per place, with contacts, jobs, emails, phones, and socials attached.
Single-dataset mode (waitForAddOns: true, the default) is designed for n8n, Zapier, and Make workflows where you don't want to join multiple datasets. The scraper waits for each add-on to finish, merges the results by CID and domain, and pushes the enriched row once everything is in. Every row also gets an _enrichment_status object showing what happened per add-on: success, no_data, failed, timed_out, or skipped.
Multi-dataset mode (waitForAddOns: false) fires the add-ons and exits. You'll find their run IDs in the key-value store under CONTACT_EXTRACTOR_RUN_ID, JOB_EXTRACTOR_RUN_ID, and EMAIL_SCRAPER_RUN_ID, and you can consume them however you want.
Emails, phones, and social links (no API key)
Enable with enableEmailExtraction: true. Pure regex, no LLM key, no AI cost. Powered by the Website Email & Phone Scraper.
Populates these fields on each place row:
emails_from_website: email addresses frommailto:links, plain text, and structured dataphones_from_website: phone numbers fromtel:links and international-format textsocial_urls:linkedin,facebook,instagram,twitter,youtubeaddress_from_website: postal address from schema.org JSON-LD when available
AI-powered contact extraction
Enable with enableContactExtraction: true and provide your own LLM API key. Powered by the Website Contact Extractor.
Populates contacts on each place row with structured records (name, position, email, phone) extracted from team, about, impressum, and contact pages.
AI-powered job listing extraction
Enable with enableJobExtraction: true and provide your own LLM API key. Powered by the Website Job Extractor.
Populates job_listings on each place row with open positions (title, location, department, job_url, application_url, posted_at, ats_system) extracted from careers pages and ATS portals: Personio, Greenhouse, Ashby, Workable, and others.
Browser fallback for JavaScript-rendered sites
Some company websites are built with React, Vue, or Angular and need a full browser to render. When enableBrowserFallback: true, the AI-powered add-ons re-scrape these sites with a real browser. Catches about 25% more sites but increases cost and runtime.
LLM API keys: bring your own
The two AI-powered add-ons need an LLM API key. You provide your own. The key is forwarded through the scraper to the AI sub-actor, and all LLM calls happen against your account. The Maps scraper itself never uses your key for anything else.
Providers (any one works; multiple enables automatic fallback):
- Gemini (recommended). Best quality-to-cost ratio, generous free tier. Get a key at Google AI Studio.
- Groq. Ultra-fast inference, generous free tier. Get a key at Groq Console.
- OpenRouter. Access to 100+ models. Get a key at OpenRouter.
If you're using OpenRouter and hit a "model not found" error, your key probably doesn't have access to the default paid Gemini model. Set llmModel to a free option like meta-llama/llama-3.1-8b-instruct, or browse available models at openrouter.ai/models.
Use cases
- Lead generation. Search for businesses by type and location. Get phone numbers, websites, and emails in one run.
- Market research. Map competitors in a geographic area with ratings, review counts, and price ranges.
- Company enrichment. Add addresses, phone numbers, GPS coordinates, and opening hours to your existing database.
- Local SEO monitoring. Track your business listing and competitor rankings across locations.
- Real estate analysis. Find nearby amenities, restaurants, and services for property listings.
- Hiring intelligence. Discover companies in a region and extract their open positions in a single pipeline.
- Review analysis. Collect review ratings and sample reviews across competitors for sentiment analysis.
Skip already-scraped places
Use the excludeCids parameter to skip places you already have. The CID (Customer ID) is a stable Google Maps identifier that doesn't change even if the business changes its name or address.
{"searchStrings": ["cafes in Zurich"],"maxResultsPerQuery": 100,"excludeCids": ["12345678901234567", "98765432109876543"]}
This is useful for incremental scraping workflows where you only want new results.
Supported languages
Pass any ISO 639-1 language code: en, de, fr, ja, es, pt, it, ko, zh-CN, ar, nl, pl, sv, da, fi, no, cs, hu, ro, el, tr, th, vi, id, and more.
Tips for better results
- Include the location in your search. "plumber Berlin" works better than just "plumber".
- Use the local language for better results in non-English countries. "Zahnarzt Zurich" beats "dentist Zurich".
- Set
languageto match your target market:defor German results,frfor French, and so on. - Use
excludeCidsto avoid re-scraping places you already have from previous runs.
Pricing
$1.00 per 1,000 places scraped. All 30+ fields are included per place. No extra charges for reviews, photos, opening hours, parsed addresses, or any other field.
Example costs:
- 100 places = $0.10
- 1,000 places = $1.00
- 10,000 places = $10.00
| Add-on | Pricing |
|---|---|
| Emails, phones, socials (regex, no LLM) | Billed separately by the Website Email & Phone Scraper |
| AI-powered contact extraction | Billed separately by the Website Contact Extractor, plus your own LLM API cost |
| AI-powered job listing extraction | Billed separately by the Website Job Extractor, plus your own LLM API cost |
Limitations
- About 120 results per query. Google Maps returns a maximum of roughly 120 places per search query. For broader coverage, split your search into multiple queries by neighborhood or sub-region.
- No individual place detail pages. Data comes from search results, not from visiting each place page individually. Most fields are populated, but some niche fields (full review text, all photos) may be limited to what Google includes in search results.
- Search relevance. Results depend on Google's ranking algorithm. The same query may return slightly different results over time.
- Rate limiting. Very high concurrency or very low request delays may trigger temporary blocks from Google. The defaults (10 concurrency, 300 ms delay) are tuned for reliability.
Related actors
Find businesses to enrich
- Kleinanzeigen.de Scraper: Germany
- Willhaben.at Scraper: Austria
- Tutti.ch Scraper: Switzerland
- Subito.it Scraper: Italy
- Marktplaats.nl Scraper: Netherlands
Real estate
- Homegate.ch Scraper: Swiss real estate
- Immowelt.de Scraper: German real estate
- Oikotie.fi Scraper: Finnish real estate
Vehicles and travel
Enrichment
Support
Found a bug or have a feature request? Open an issue.