Google Maps Local Business Leads Scraper avatar

Google Maps Local Business Leads Scraper

Pricing

from $1.80 / 1,000 business results

Go to Apify Store
Google Maps Local Business Leads Scraper

Google Maps Local Business Leads Scraper

Scrape local business leads from Google Maps by keyword and location. Export clean, flat, CSV-friendly business data for outreach, SEO, web design sales, competitor research, and local lead generation.

Pricing

from $1.80 / 1,000 business results

Rating

0.0

(0)

Developer

Delowar Munna

Delowar Munna

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Google Maps Local Business Leads Scraper

Lead-focused Google Maps scraping by keyword + location. Returns a clean, flat, CSV-friendly business row per result — built for outreach, SEO, web design sales, competitor research, and local lead generation.

V1 deliberately avoids website crawling, email extraction, full review scraping, photos, menus, and AI enrichment so the run stays fast and cheap. You only pay for unique business rows that pass your filters and are successfully written to the dataset.

✨ Why this scraper

  • Lead-focused — 28 flat fields tuned for cold outreach (phone, website, rating, review count, address parts, lead score, reason tags).
  • Pay-Per-Event$ per saved unique business row. Duplicates, filtered rows, and failed pushes are not charged.
  • No login, no cookies, no sessions — just keyword + location.
  • CSV-friendly output — flat structure, no nested objects, drops cleanly into Sheets/Excel/CRMs.
  • Transparent lead score — rule-based (no AI), explained below.

🚀 Quick start — sample inputs

Example 1 — single query

{
"searchQueries": [
{ "key": "plumbers", "value": "Canberra ACT" }
],
"maxResults": 100,
"country": "AU",
"language": "en",
"websiteFilter": "any",
"phoneRequired": false,
"includeOpeningHours": true,
"includeCoordinates": true,
"includeLeadScore": true,
"deduplicateResults": true,
"proxyConfiguration": { "useApifyProxy": true }
}

Example 2 — multi-query, phone required, only businesses without a website (web-design lead list)

{
"searchQueries": [
{ "key": "electricians", "value": "Sydney NSW" },
{ "key": "dentists", "value": "Melbourne VIC" },
{ "key": "cafes", "value": "Brisbane QLD" }
],
"maxResults": 200,
"country": "AU",
"language": "en",
"minRating": 4.0,
"minReviewCount": 10,
"websiteFilter": "missingWebsite",
"phoneRequired": true,
"categoryFilter": "",
"includeOpeningHours": true,
"includeCoordinates": true,
"includeLeadScore": true,
"deduplicateResults": true,
"proxyConfiguration": { "useApifyProxy": true }
}

The searchQueries field uses Apify's Key/Value form editor — the Key column is the business keyword (e.g. plumbers), the Value column is the location (e.g. Canberra ACT). Add one row per search.


📦 Output

The dataset has one view: Business leads — a 28-column flat table.

Business leads table view

Sample record — Business leads

{
"search_keyword": "electricians",
"search_location": "Sydney NSW",
"business_name": "Hello Electrical | Electrician Sydney",
"category": "Electrician",
"rating": 5,
"review_count": 442,
"phone": "+61 2 8000 1185",
"website": "https://www.helloelectrical.com.au/",
"website_domain": "helloelectrical.com.au",
"address": "192 Edgeware Rd, Newtown NSW 2042, Australia",
"street_address": "192 Edgeware Rd",
"suburb": "",
"city": "Newtown",
"state": "NSW",
"postcode": "2042",
"country": "Australia",
"google_maps_url": "https://www.google.com/maps/place/Hello+Electrical+%7C+Electrician+Sydney/",
"place_id": "0x6b0d57cca361e2b1:0x3828b97e7714dc6d",
"latitude": -33.9044935,
"longitude": 151.1733881,
"opening_hours": "Closes 5 pm",
"is_open_now": true,
"has_website": true,
"has_phone": true,
"lead_quality_score": 100,
"lead_quality_label": "Strong",
"reason_tags": [
"has_phone",
"has_website",
"high_rating",
"many_reviews",
"complete_address",
"category_found",
"opening_hours_found",
"outreach_ready"
],
"scraped_at": "2026-05-05T05:20:53.092Z"
}

Output fields (28)

search_keyword, search_location, business_name, category, rating, review_count, phone, website, website_domain, address, street_address, suburb, city, state, postcode, country, google_maps_url, place_id, latitude, longitude, opening_hours, is_open_now, has_website, has_phone, lead_quality_score, lead_quality_label, reason_tags, scraped_at.


🎯 Lead quality score

Transparent rule-based score (0–100) computed from visible fields — no AI, no external enrichment.

SignalPoints
Has phone+20
Has website+20
Rating ≥ 4.3+15
Review count ≥ 30+15
Has full address+10
Category found+10
Opening hours found+10

Labels: Strong (80–100) · Good (60–79) · Moderate (40–59) · Weak (0–39). Reason tags include outreach_ready when has_phone, business_name, and address are all present — sort by this tag to get a clean call/email list.


💰 Pricing

Pay-Per-Event. The actor charges a single business-result event for each unique, filtered business row that is successfully written to the dataset. The actor honors the user-configured per-run spending cap (Apify eventChargeLimitReached) and stops cleanly when reached.

Not charged:

  • Duplicates (de-duplicated by place_id, listing URL, or name+address).
  • Rows filtered out by minRating, minReviewCount, websiteFilter, phoneRequired, or categoryFilter.
  • Rows missing a business_name.
  • Failed dataset pushes.
  • Anything after the per-run spending cap is reached.

📊 Run summary

After each run, a RUN_SUMMARY entry is written to the key-value store:

{
"search_queries_total": 3,
"successful_queries": 3,
"failed_queries": 0,
"raw_results_found": 240,
"results_saved": 187,
"duplicates_removed": 21,
"filtered_out": 32,
"charged_events": 187,
"blocked_requests": 0,
"retried_requests": 4,
"runtime_seconds": 142,
"scraped_at": "2026-05-05T12:00:00.000Z"
}

charged_events always equals results_saved.


⚙️ Filters

FilterEffect
minRatingDrop businesses below this star rating (0 disables).
minReviewCountDrop businesses with fewer reviews (0 disables).
websiteFilterany / hasWebsite / missingWebsite.
phoneRequiredIf true, keep only businesses with a visible phone.
categoryFilterCase-insensitive contains match against the business category.
deduplicateResultsDrop duplicates across queries (recommended ON).

Filters are applied after extraction and before dataset push or event charge.


🚧 Limitations (V1)

  • Cards-only extraction: V1 reads each business row directly from the search results panel and does not click into individual place detail panels. Phone, website, full opening hours, and place_id only appear when Google surfaces them on the card itself; otherwise these fields are empty/null.
  • No website crawling, email extraction, or social link extraction.
  • No full review text or sentiment.
  • No photos, menus, prices, or popular times.
  • No login/cookie/session-based scraping.
  • Address parsing into street/suburb/city/state/postcode is best-effort; the full address field is the source of truth.
  • Per-query hard cap is 500 results; per-run hard cap is 5,000 results.

🛠️ Technical notes

  • Stack: Node.js 22 · Apify SDK 3 · Crawlee · Puppeteer.
  • Concurrency: min=1, max=3 (conservative to reduce blocking).
  • Memory: 1 GB min · 2 GB default · 4 GB max.
  • Proxy: Apify Proxy enabled by default; custom configs accepted.
  • Diagnostics: On the first failed render (no feed, or feed but zero cards), the actor saves the page HTML and URL to the key-value store as debug-no-feed-html / debug-zero-cards-html.