Google Maps Scraper | $1/1k Places avatar

Google Maps Scraper | $1/1k Places

Pricing

from $0.75 / 1,000 place scrapeds

Go to Apify Store
Google Maps Scraper | $1/1k Places

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

NanoScrape

Maintained by Community

Actor stats

9

Bookmarked

461

Total users

145

Monthly active users

8 hours

Issues response

7 hours ago

Last modified

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-scraper to 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

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"
}

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

ParameterTypeDefaultDescription
searchStringsstring[][]Simple search strings. Include the location in the string for geo-targeting.
queriesobject[][]Structured queries with optional query, location, country, company_id.
maxResultsPerQueryinteger20Results per query. Max 120 (Google Maps page limit).
maxTotalResultsinteger(none)Optional global cap across all queries combined. Leave empty for no cap. Useful for budget control.
maxResultsinteger(none)Deprecated alias for maxResultsPerQuery. Still works for backward compatibility.
languagestring"en"ISO 639-1 language code (en, de, fr, ja, es, pt, it, ko, zh, ar, etc.).
includeReviewCountbooleantrueEnrich each result with review_count via the Maps Embed API. Adds about 150 ms per place.
includePopularTimesbooleanfalseOpt-in. Enrich each result with the visitor-traffic histogram by weekday × hour (0–100). Adds about 1.5 s per place.
includeReviewsbooleanfalseOpt-in. Fetch featured reviews for each place (author, rating, date, text). Adds about 300 ms per place.
maxReviewsPerPlaceinteger25Maximum reviews per place. Set to 0 for no cap.
reviewsSortstringmost_relevantSort order: most_relevant, newest, highest_rating, lowest_rating.
reviewsNewerThanstring(none)Only return reviews on or after this date (YYYY-MM-DD). Auto-switches sort to newest for an efficient cutoff.
enableEmailExtractionbooleanfalseRegex extractor for emails, phone numbers, and social URLs (LinkedIn, Facebook, Instagram, Twitter, YouTube). No LLM key required.
enableContactExtractionbooleanfalseAI-powered structured contact extraction (name, position, email, phone). Requires your own LLM API key.
enableJobExtractionbooleanfalseAI-powered job-listing extraction from careers pages. Requires your own LLM API key.
enableBrowserFallbackbooleanfalseRe-scrape JS-rendered sites (React, Vue, Angular) with a full browser. Catches about 25% more sites.
outputLanguagestring"en"Language for AI-extracted fields (en, de, fr, it, es, pt, nl, auto).
geminiApiKeystring(none)Your Gemini API key. Free tier is generous, so this is the recommended starting point.
groqApiKeystring(none)Your Groq API key. Ultra-fast inference with a free tier.
openrouterApiKeystring(none)Your OpenRouter API key. Access to 100+ models. Optional fallback.
llmModelstring(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.
waitForAddOnsbooleantrueWhen 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.
addOnWaitCapMsinteger1800000Safety cap on how long to wait for add-ons before flushing remaining places with a timed_out status. Default 30 min.
excludeCidsstring[][]Google CIDs to skip. Useful for excluding already-scraped places.
maxConcurrencyinteger10Parallel requests (1–20).
requestDelayinteger300Delay between pagination requests in milliseconds (100–10,000).
proxyConfigurationobjectApify proxyProxy 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

FieldTypeDescription
company_idstringYour reference ID (from queries) or the search string
titlestringBusiness name
categorystringPrimary Google Maps category
categoriesstring[]All assigned categories
addressstringFull formatted address
complete_addressobjectParsed components: street, city, postal_code, state, country
latitudenumberGPS latitude
longitudenumberGPS longitude
plus_codestringGoogle Plus Code
timezonestringIANA timezone identifier
phonestringPhone number
websitestringWebsite URL
emailsstring[]Email addresses found on the listing
open_hoursobjectOpening hours grouped by day of week
popular_timesobjectVisitor traffic patterns (opt-in via includePopularTimes)
price_rangestringPrice level ($ to $$$$)
statusstringOPERATIONAL, CLOSED_TEMPORARILY, or CLOSED_PERMANENTLY
review_countintegerTotal number of reviews
review_ratingnumberAverage rating (1.0 to 5.0)
reviews_per_ratingobjectBreakdown of reviews by star rating (1 through 5)
reviews_linkstringDirect URL to all reviews
user_reviewsarraySample reviews with name, rating, text, date, owner response
thumbnailstringMain business photo URL
imagesarrayAdditional photos with titles
reservationsarrayReservation links with source (TheFork, OpenTable, etc.)
order_onlinearrayOnline ordering links with source
menuobjectMenu link and source
ownerobjectBusiness owner name and profile link
aboutarrayAmenities and features (dine-in, Wi-Fi, wheelchair access, etc.)
descriptionstringBusiness description from Google
linkstringFull Google Maps URL for this place
cidstringGoogle Maps CID: a stable identifier that persists across name and address changes
data_idstringGoogle internal data ID
searchStringstringThe search query that produced this result
_rankintegerPosition in search results (1-based)
scraped_atstringISO 8601 timestamp
emails_from_websitestring[]Emails extracted from the company's website. Add-on: email extractor
phones_from_websitestring[]Phone numbers extracted from the company's website
social_urlsobjectSocial profiles from the website: linkedin, facebook, instagram, twitter, youtube
address_from_websitestringPostal address from schema.org data on the website
contactsarrayAI-extracted structured contacts from the website (name, position, email, phone). Add-on: contact extractor
job_listingsarrayAI-extracted job listings from the careers page. Add-on: job extractor
_enrichment_statusobjectPer-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).

  • maxReviewsPerPlace caps the number of reviews per place (default 25). Set to 0 for no cap. The actual number returned depends on how many featured reviews Google surfaces, typically up to 8 for high-traffic places.
  • reviewsSort controls 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 to newest so 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.

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 from mailto: links, plain text, and structured data
  • phones_from_website: phone numbers from tel: links and international-format text
  • social_urls: linkedin, facebook, instagram, twitter, youtube
  • address_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):

  1. Gemini (recommended). Best quality-to-cost ratio, generous free tier. Get a key at Google AI Studio.
  2. Groq. Ultra-fast inference, generous free tier. Get a key at Groq Console.
  3. 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 language to match your target market: de for German results, fr for French, and so on.
  • Use excludeCids to 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-onPricing
Emails, phones, socials (regex, no LLM)Billed separately by the Website Email & Phone Scraper
AI-powered contact extractionBilled separately by the Website Contact Extractor, plus your own LLM API cost
AI-powered job listing extractionBilled 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.

Find businesses to enrich

Real estate

Vehicles and travel

Enrichment

Support

Found a bug or have a feature request? Open an issue.