Google Maps Leads + Contact Enrichment avatar

Google Maps Leads + Contact Enrichment

Pricing

from $1.40 / 1,000 results

Go to Apify Store
Google Maps Leads + Contact Enrichment

Google Maps Leads + Contact Enrichment

Turns any Google Maps search into a local lead list: name, phone, website, full address, coordinates, rating, review count, categories, opening hours and place/CID ids. Optionally visits each business's own website to pull the email addresses and social profiles Google never exposes.

Pricing

from $1.40 / 1,000 results

Rating

0.0

(0)

Developer

Ibnu Adzim

Ibnu Adzim

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 days ago

Last modified

Categories

Share

Turns any Google Maps search into a local B2B lead list — and, optionally, finds the email addresses Google Maps itself never exposes by visiting each business's own website.

What you get

One row per business (recordType: PLACE):

GroupFields
Identityname, placeId, cid, featureId, knowledgeGraphId, mapsUrl, cidUrl
Contactphone, phoneE164, website, websiteDomain
Enriched contactemails, socialProfiles (facebook / instagram / twitter / linkedin / youtube / tiktok / pinterest / whatsapp), enrichedFrom, enrichmentStatus
LocationfullAddress, streetAddress, addressLines, addressParts, neighbourhood, cityRegion, countryCode, latitude, longitude, timezone
Reputationrating, reviewCount, reviewsUrl
OperationsopeningHours, openStatus, attributes (accessibility, service options, …), categories, primaryCategory

Plus one SEARCH_SUMMARY row per query (what was asked, what came back, which honesty flags fired) and an ERROR row for any query that fails — so every input maps to at least one row.

Example input

{
"searchQueries": ["dentist Austin TX", "plumber Manchester UK"],
"enrichContacts": true,
"maxPlacesPerQuery": 100
}

Known limits — read these before you buy

  • ~200 results per query. Google's own ceiling for a single query and viewport, measured by walking offsets 0→200: pages stay full to offset 160, offset 180 returns a short page, offset 200 returns nothing. It ends honestly — no clamp back to page 1. To go wider, split the area: several narrow queries, or explicit latitude/longitude per district. The perQueryCeilingReached flag on the summary row tells you when you hit it.
  • reviewCount is best-effort. Google alternates between two response variants for the identical request: one carries the review count, one does not. It is not a cookie, not the request shape, and not a clean function of time — all three were tested. The actor re-issues any thin page once, then reports the truth in reviewCountsAvailable and placesWithReviewCount rather than emitting a silent null. Ratings are unaffected — they are present in both variants.
  • openingHours may be today-only. The same two variants: the rich one carries all seven days, the thin one carries today. The field reflects whichever arrived; it never pads a partial week into a full-looking one.
  • No totalMatches. Google publishes no match count for a local search, so this actor does not invent one — upstreamTotalAvailable is always false.
  • The viewport is not a geocode. Without explicit latitude/longitude, the map centre comes back as the country centroid for your countryCode, and the query text does the geographic work. viewportSource says which applied. "dentist Austin TX" returns Austin businesses over a nationwide viewport — verified 30/30.
  • Enrichment is bounded and polite. Up to 3 pages per site (homepage, then /contact, /contact-us, /about, /about-us, /impressum), stopping as soon as both an email and a social profile are found. Every third-party host's robots.txt is fetched and honoured first; a disallowed site is reported as enrichmentStatus: robots_disallowed, not scraped anyway. Roughly 60% of businesses yield an email in practice — the rest have no site, a JS-only contact form, or no address published.

How it works

No API key, no login, no browser.

  1. GET /maps/search/<query> — the Maps shell, for the ei session token.
  2. GET /search?tbm=map&tch=1&pb=<protobuf> — the results endpoint, paged by offset, 100 places per request.

tch=1 is load-bearing: without it the same request answers 200 with no results array at all — a silent empty rather than an error. 5.4 KB and zero places without it, 315 KB and a full page with it.

The pb protobuf is deliberately minimal. A 90-token capture of the real web client's pb was tried first and returned nothing, because hand-editing it desynchronised the m<N> sub-message counts. Bisecting down, only the viewport block, the !7i/!8i pagination pair and the !22m6 session block actually matter.

WAF and TLS

None observed. chrome124, chrome131, chrome136, firefox133, safari18_0 and chrome99_android all returned identical 200s on both surfaces, with no challenge markers and no cookie warmup needed. The profile pool exists for transport flakiness, not for a bypass. Default chrome124.

Google rate limits by exit IP, and answers a hot address with a 302 to /sorry/index (a reCAPTCHA interstitial) rather than an HTTP error — detected on the redirect target, since the followed body looks like a normal 200. The actor rotates the exit IP on that signal. Residential proxy is the default on the platform for the same reason. Raise minRequestInterval if you see the warning.

Policy

www.google.com/robots.txt carries no blanket Disallow for ClaudeBot, anthropic-ai, GPTBot or CCBot, and both surfaces this actor uses are explicitly allowed:

Allow: /search?*tbm=map
Allow: /maps/search/
Allow: /maps/place/

Only public business listings are read. No login, no user data, no reviews text, no personal profiles.