Google Maps Lead Scraper - Extract Business Leads avatar

Google Maps Lead Scraper - Extract Business Leads

Pricing

from $0.0001 / actor start

Go to Apify Store
Google Maps Lead Scraper - Extract Business Leads

Google Maps Lead Scraper - Extract Business Leads

Pull thousands of business leads from Google Maps in minutes. Pick a query and a region, get every business with name, phone, website, address, rating, reviews, and place ID. No browser, no captcha, no API quota. Any country, state, city, or custom area. Optional owner-reply check.

Pricing

from $0.0001 / actor start

Rating

0.0

(0)

Developer

Muhammad Yaser

Muhammad Yaser

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Categories

Share

Google Maps Lead Scraper — Bulk Business Leads to CSV

Scrape unlimited Google Maps business leads from any region. Tile a bounding box (country, state, city, custom area) into a lat/lng grid, hit Google's internal search?tbm=map endpoint over plain HTTP, and dedupe by placeId. Pull business name, phone number, website, address, rating, review count, lat/lng, and place ID for every business Google Maps knows about — no browser, no captcha hell, no Google Maps API quota.

Why this scraper is different: normal Google Maps scraping is capped at ~100 results. The official Google Maps API charges per request and has tight quotas. This Actor bypasses both by tiling the region into a grid of small lat/lng cells and pulling 20 results per cell — thousands of unique leads per run, deduplicated by placeId.

Use cases: B2B sales lead generation · local business research · marketing lists · real estate comps · franchise research · competitor analysis · outreach prospecting.

Try it free — default input is query="plumbers", region "washington", no card required during the Apify free trial.


What this Actor extracts

For each business, you get:

  • name — business name
  • category — Google Maps category (e.g. Plumber, Restaurant)
  • phone — phone number
  • website — business website URL
  • address — full street address
  • rating — Google star rating (e.g. 4.8)
  • reviews — total review count
  • lat, lng — latitude and longitude
  • placeId — Google's stable place ID (use this to fetch more later)
  • mapsUrl — shareable Google Maps URL
  • ownerReplied — (optional) 1 if the owner has replied to any review, 0 if not, null if not checked

How it works

  1. Splits the bounding box into a grid of lat/lng tiles.
  2. Fires one HTTP request per tile, paginating 20 results at a time per tile while pages come back full.
  3. Deduplicates by placeId so overlapping tiles never produce duplicates.
  4. Pushes each unique lead to the default Dataset and (optionally) writes a CSV to the default Key-Value Store.
  5. If checkOwnerReplies=true, fires one extra HTTP request per lead to fetch that place's review data and mark whether the owner has replied to any review.

This breaks the ~100-result cap of normal Google Maps scraping by hitting Google's internal search endpoint, not the public site.

Why use this Actor instead of the Google Maps API or other scrapers?

This ActorGoogle Maps Places APIBrowser-based scrapers
CostFree during trial, then $5/mo+$0.007–$0.032 per requestTooling + proxy costs
QuotaNone$200/mo free credit, then pay-per-callDepends on proxy
Result capNone (tiles the region)60 results per query~100 per session
OutputDataset + CSVJSONCSV / DB
Phone, website, rating, reviews✅ all included⚠️ some fields require separate calls
Owner-reply check✅ optional
Set-upZero codeAPI key + billingLocal browser + config

If you need more than ~100 leads per search, or you don't want to manage a Google Cloud billing account, this Actor is a better fit.

Example queries (just paste in the input)

VerticalTry
Home servicesplumbers, electricians, hvac repair, roofing contractors, locksmith, pest control, landscaping, painters
Health & medicaldentists, chiropractors, dermatologists, urgent care, physical therapy, optometrists
Real estatereal estate agents, property management, real estate attorneys, title company
Autoauto repair, tire shops, car dealerships, car wash, body shops
Food & hospitalityrestaurants, cafes, bars, caterers, food trucks, wedding venues
Professional servicesaccountants, lawyers, financial advisors, tax preparation, notary
Retailflorists, boutique stores, furniture stores, jewelry stores, pet stores
B2B / industrialmanufacturers, warehouses, machine shops, commercial cleaning

Any service-business query works — Google Maps indexes tens of millions of them. Use a custom region to target a single city, state, country, or your own lat/lng bounding box.

Input

FieldTypeDefaultNotes
querystringplumbersSearch term, e.g. plumbers, hvac repair, restaurants
regionstringwashingtonPreset name (washington, gujrat-pk) or a custom spec: custom:latMin,latMax,lngMin,lngMax,latStep,lngStep
maxPagesPerTileint520 results per page. 5 = up to 100/tile. Higher = more uniques in dense cities but more requests.
saveCsvbooltrueAlso write <query>-<region>.csv to the default Key-Value Store
checkOwnerRepliesboolfalseFor each lead, fetch place details and set ownerReplied=1 if the owner has replied to at least one review, 0 if not, null if undetermined. Requires pb-place-template.json (see below). Adds ~1 extra HTTP request per lead.

For a custom region, get the bbox from OpenStreetMap's export view and pick latStep / lngStep based on density (~0.3°/0.45° is ~33 km — good for whole states; ~0.08°/0.09° is ~9 km — good for dense cities).

Output

Default Dataset — one item per lead, columns:

name, category, phone, website, address, rating, reviews, lat, lng, placeId, mapsUrl, ownerReplied

ownerReplied is 1 (owner has replied to at least one review), 0 (no reply detected), or null (not checked / undetermined).

Default Key-Value Store (when saveCsv is true) — key <query>-<region>.csv with the same columns.

Local run

Requires Node 20+ and the Apify CLI:

npm install
npx apify run # uses .actor/input_schema.json defaults

To override input, create .actor/input.json (or storage/key_value_stores/default/INPUT.json) or set APIFY_INPUT_JSON to a JSON string.

$APIFY_INPUT_JSON='{"query":"hvac repair","region":"washington","maxPagesPerTile":3}' npx apify run

Deploy to Apify

npx apify login
npx apify push

The CLI reads .actor/actor.json, builds the Dockerfile, and pushes the Actor to your Apify account.

Refresh the internal-search template

The Actor ships with pb-template.json — a captured internal request parameter Google uses to authenticate search?tbm=map calls. It expires periodically (days to weeks). When responses stop starting with )]}', refresh:

  1. Locally, in the source project (gmaps-crawlee):
    $node src/bootstrap-pb.js
  2. Copy the new pb-template.json into this Actor at the project root.
  3. Re-deploy with npx apify push.

Enable the owner-reply check (checkOwnerReplies)

The owner-reply check is off by default and requires a second captured template, pb-place-template.json, that the Actor ships as an empty placeholder. To enable it:

  1. Locally, in this Actor (gmaps-crawlee-apify):
    $node src/bootstrap-pb-place.js
    The script opens Chrome, searches Google Maps, clicks the first result, and captures the XHR that loads that place's details (including its reviews and owner replies). It saves the parameterized pb to pb-place-template.json.
  2. Re-deploy with npx apify push.
  3. Set checkOwnerReplies to true in the Actor's input.

The Actor will then make one extra HTTP request per scraped lead to check for owner replies. Each lead in the Dataset gets an ownerReplied field:

  • 1 — owner has replied to at least one review
  • 0 — no reply detected
  • null — could not determine (template expired, captcha, or parsing miss)

Why an Actor (and not just the CLI)?

  • Scheduling — run on a cron, e.g. weekly "fresh leads in my area".
  • Webhooks — pipe new datasets to Slack / Sheets / a CRM.
  • Billing — charge per run / per 1k results once published.
  • Concurrency — Apify's runtime handles memory + headless cleanly.

Notes & limits

  • No proxies are bundled. If you hit captchas at scale, add proxyConfiguration to the HttpCrawler options and price accordingly.
  • Tile size matters: smaller latStep/lngStep = more uniques in dense cities, more requests. Tune region for the geography.
  • Dedupe is by placeId. Re-runs and overlapping tiles never duplicate.
  • The bundled pb-template.json was captured against Google Maps' internal search?tbm=map endpoint. It is bound to the same public Terms of Service considerations as any Google Maps scraper.

Project layout

gmaps-crawlee-apify/
├── .actor/
│ ├── actor.json # Apify Actor manifest
│ └── input_schema.json # Input UI schema
├── Dockerfile # apify/actor-node:20
├── package.json
├── pb-template.json # captured internal search template
├── pb-place-template.json # captured place-details template (for owner-reply check)
├── README.md
└── src/
├── main.js # scraper, wrapped in Actor.main()
└── bootstrap-pb-place.js # one-time capture of pb-place-template.json