# Google Maps Contact Enrichment — Business Lead Finder (`xtracto/gmaps-contact-enrichment`) Actor

Find businesses on Google Maps, then pull an email address and social media links straight off their website. Turns a Maps search into a ready-to-use B2B contact list.

- **URL**: https://apify.com/xtracto/gmaps-contact-enrichment.md
- **Developed by:** [Farhan Febrian Nauval](https://apify.com/xtracto) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $6.67 / 1,000 results

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Google Maps Contact Enrichment

Find businesses on Google Maps and get back their email address and social media links — pulled straight from their own website. Turns a Maps search into a ready-to-use B2B contact list.

### Why use this actor

- **Search or direct lookup** — search by business type + location (e.g. "dentist" in "New York, NY"), or pass exact Google Maps links / place IDs if you already have them.
- **No account or login required** — nothing to configure, nothing to authenticate.
- **Finds the website for you** — most Maps listings don't show their website field directly; this actor pulls it from the place's full detail page automatically.
- **Real contact data, not guesses** — email addresses (including simple "name \[at] domain \[dot] com" style hiding) and social profile links (Facebook, Instagram, LinkedIn, X/Twitter, TikTok, YouTube), read from the actual homepage and a linked contact/about page.
- **Honest results** — if a business's website doesn't publish any contact info, you get a clear `"no_contact_found"` status back, not a fabricated guess.
- Stable JSON output, ready for CRM import or a cold-outreach pipeline.

### How it works

1. You give the actor a business type + location (search mode) or a list of exact Google Maps links / place IDs (direct mode).
2. In search mode, the actor looks up matching places on Google Maps, then fetches each place's full profile to get its website (most search results don't carry a website field on their own).
3. For every website found, the actor fetches the homepage — and a contact/about page if the homepage links to one — and scans the page for email addresses and social profile links.
4. Every place becomes one row in your dataset, whether or not contact info was found, so you always know exactly what was checked and what came back empty.

You don't need to manage any browser, proxy, or scraping infrastructure — just give it a query and get a dataset back.

### Known limitations (read this before relying on the numbers)

- **Not every business site publishes an email or social link on its homepage/contact page** — some hide it behind a contact form only, some only show a phone number. This is a limitation of the target sites, not the actor.
- **JavaScript-only contact widgets are not rendered** — this actor reads the page's HTML, it does not run a browser. If a site injects its email or social icons purely via client-side JavaScript with no fallback in the page source, it won't be picked up.
- **Some sites actively block automated requests** (`site_unreachable` status) — a handful of business websites return 403s or time out for any non-browser request, regardless of scraper.
- **Real observed hit rate**: across 56 real business websites tested (dentists in New York, plumbers in Austin, coffee shops in Seattle), **71% returned at least one email or social link** (41% an email, 66% a social link). Your results will vary by industry — service businesses with active social media (plumbers, coffee shops) tend to score higher than smaller practices with bare-bones sites.

### Input

#### Search mode (default)

```json
{
    "mode": "search",
    "query": "dentist",
    "location": "New York, NY",
    "maxItems": 20,
    "maxConcurrency": 5,
    "language": "en",
    "country": "us"
}
```

#### Direct mode

```json
{
    "mode": "direct",
    "placeUrls": [
        "https://www.google.com/maps/place/Empire+State+Building/@40.7484,-73.9857,17z"
    ],
    "maxItems": 20
}
```

| Field | Type | Description |
|---|---|---|
| `mode` | string | `"search"` (default) — find businesses by keyword + location. `"direct"` — pass exact Maps URLs or item_ids and skip search. |
| `query` | string | Business type/keyword to search for. Only used in `search` mode. |
| `location` | string | City/area text (e.g. `"New York, NY"`), or exact `"lat,lng"` coordinates. Only used in `search` mode. |
| `placeUrls` | array | Full Google Maps place URLs, or item_ids (`0x...:0x...`). Only used in `direct` mode. |
| `maxItems` | integer | Max number of places to look up and enrich in this run. Default: 20. |
| `maxConcurrency` | integer | How many places to process in parallel. Default: 5. |
| `language` | string | Language for the Google Maps result text. Default: `en`. |
| `country` | string | Geographic bias for Google Maps results. Default: `us`. |

### Output

Real sample from a `search` run (`query: "plumber"`, `location: "Austin, TX"`):

```json
{
    "_input": "plumber | Austin, TX",
    "_scrapedAt": "2026-08-06T09:53:56Z",
    "placeName": "Beyond Wow Plumbing & Drains",
    "placeAddress": "3432 Greystone Dr, Austin, TX 78731",
    "category": "Plumber",
    "phone": "(512) 601-6173",
    "mapsUrl": "https://www.google.com/maps/place/Beyond+Wow+Plumbing+%26+Drains/@30.3578911,-97.7482485,14z/data=...",
    "website": "https://beyondwow.com/?utm_campaign=gmb&utm_medium=gbp-austin&utm_source=google",
    "contactEmails": ["info@beyondwow.com"],
    "socialLinks": {
        "facebook": "https://www.facebook.com/beyondwowplumbing",
        "instagram": "https://www.instagram.com/beyondwowplumbing",
        "tiktok": "https://www.tiktok.com/@beyondwowplumbing",
        "youtube": "https://www.youtube.com/@beyondwowplumbing",
        "linkedin": "https://www.linkedin.com/company/beyondwowplumbing"
    },
    "pagesChecked": [
        "https://beyondwow.com/?utm_campaign=gmb&utm_medium=gbp-austin&utm_source=google",
        "https://beyondwow.com/contact/"
    ],
    "enrichmentStatus": "found"
}
```

Real example where the website was reachable but published no contact info at all (an honest "nothing found" result, not an error):

```json
{
    "_input": "plumber | Austin, TX",
    "placeName": "Rooter-Man Plumbing Austin TX",
    "placeAddress": "15503 Patrica St, Austin, TX 78728",
    "category": "Plumber",
    "phone": "(512) 720-7092",
    "website": "http://rooterman.com/austin/",
    "contactEmails": [],
    "socialLinks": {},
    "pagesChecked": ["https://rootermanofaustin.com/"],
    "enrichmentStatus": "no_contact_found"
}
```

| Field | Type | Description |
|---|---|---|
| `placeName` | string | Business name from Google Maps. |
| `placeAddress` | string | Business address from Google Maps. |
| `category` | string | Business category/type (e.g. "Plumber", "Dentist"). |
| `phone` | string | Phone number, from Google Maps (not the website). |
| `mapsUrl` | string | Link back to the Google Maps listing. |
| `website` | string | null | The business's website, as listed on Google Maps. `null` if Maps has no website on file. |
| `contactEmails` | array | Email addresses found on the website. Empty if none found. |
| `socialLinks` | object | Social profile links found, keyed by platform (`facebook`, `instagram`, `linkedin`, `twitter`, `tiktok`, `youtube`). Empty if none found. |
| `pagesChecked` | array | Which page(s) were actually fetched (homepage, and a contact/about page if one was found and followed). |
| `enrichmentStatus` | string | `"found"` (email or social found), `"no_contact_found"` (website checked, nothing published), `"no_website"` (Maps has no website on file), `"site_unreachable"` (website blocked the request, errored, or timed out), `"no_places_found"` / `"maps_search_failed"` / `"maps_lookup_failed"` (search/lookup problems, see `_error`). |
| `_input` | string | The query + location (search mode) or the URL/item_id (direct mode) that produced this row. |
| `_scrapedAt` | string | UTC timestamp of when this row was collected. |

### Tips

- If you already run [Google Maps Search Places](https://apify.com/xtracto/gmaps-search-places), feed its `item_id` values straight into `direct` mode here to skip the search step.
- Raise `maxConcurrency` for faster runs on well-behaved sites; lower it if you're seeing a lot of `site_unreachable` results (some anti-bot-protected sites rate-limit aggressively).
- `enrichmentStatus: "no_website"` means the fix is on the Google Maps listing itself, not this actor — the business hasn't added a website to their profile.

### Pair with other actors

- Use **[Google Maps Search Places](https://apify.com/xtracto/gmaps-search-places)** first if you want the full raw list of places before deciding which ones to enrich.
- Use **[Google Maps Place Detail](https://apify.com/xtracto/gmaps-detail-places)** if you only need the Maps profile (hours, popular times, ratings) without the website enrichment step.

# Actor input Schema

## `mode` (type: `string`):

search = find businesses by keyword + location on Google Maps. direct = pass exact Google Maps URLs or item_ids, skip the search step.

## `query` (type: `string`):

What kind of business to look for, e.g. 'dentist', 'plumber', 'coffee shop'.

## `location` (type: `string`):

City/area text (e.g. 'New York, NY') or exact coordinates as 'lat,lng'.

## `placeUrls` (type: `array`):

Full Google Maps place URLs, or item_ids in the format 0x...:0x.... Used only when mode = direct.

## `maxItems` (type: `integer`):

Upper limit on how many places get looked up and enriched in this run.

## `maxConcurrency` (type: `integer`):

How many places to process in parallel. Lower it if you see a lot of blocked websites.

## `language` (type: `string`):

Language for the Google Maps result text (names, addresses).

## `country` (type: `string`):

Geographic bias for Google Maps results.

## Actor input object example

```json
{
  "mode": "search",
  "query": "dentist",
  "location": "New York, NY",
  "placeUrls": [
    "https://www.google.com/maps/place/Empire+State+Building/@40.7484,-73.9857,17z"
  ],
  "maxItems": 20,
  "maxConcurrency": 5,
  "language": "en",
  "country": "us"
}
```

# Actor output Schema

## `placeName` (type: `string`):

Place Name as reported by the source.

## `placeAddress` (type: `string`):

Place Address.

## `category` (type: `string`):

Category the item belongs to.

## `phone` (type: `string`):

Phone number as published by the business.

## `website` (type: `string`):

Website URL.

## `contactEmails` (type: `string`):

Contact Emails.

## `socialLinks` (type: `string`):

Social Links.

## `enrichmentStatus` (type: `string`):

Enrichment Status.

## `mapsUrl` (type: `string`):

Link to the maps.

## `_scrapedAt` (type: `string`):

UTC timestamp of the scrape, ISO 8601.

## `pagesChecked` (type: `string`):

Pages Checked as reported by the source.

## `name` (type: `string`):

Name of the item.

## `address` (type: `string`):

Street address.

## `item_id` (type: `string`):

Item id as reported by the source.

## `search_path` (type: `string`):

Search path as reported by the source.

## `place_id` (type: `string`):

Place id as reported by the source.

## `coordinate` (type: `string`):

Coordinate as reported by the source.

## `review_count` (type: `string`):

Review count. Whole number.

## `rating_star` (type: `string`):

Rating star as reported by the source.

## `region` (type: `string`):

Region as reported by the source.

## `scheduler` (type: `string`):

Scheduler as reported by the source.

## `status` (type: `string`):

Status reported by the source.

## `detail` (type: `string`):

Detail as reported by the source.

## `tags` (type: `string`):

Tags or keywords. List of values.

## `description` (type: `string`):

Short description or summary.

## `hotel_detail` (type: `string`):

Hotel detail as reported by the source.

## `restaurant_detail` (type: `string`):

Restaurant detail as reported by the source.

## `popular_times` (type: `string`):

Popular times.

## `popular_real_time` (type: `string`):

Popular real time as reported by the source.

## `url` (type: `string`):

Direct link to the scraped item.

## `_input` (type: `string`):

The input value this row was produced from.

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "query": "dentist",
    "location": "New York, NY",
    "placeUrls": [
        "https://www.google.com/maps/place/Empire+State+Building/@40.7484,-73.9857,17z"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("xtracto/gmaps-contact-enrichment").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "query": "dentist",
    "location": "New York, NY",
    "placeUrls": ["https://www.google.com/maps/place/Empire+State+Building/@40.7484,-73.9857,17z"],
}

# Run the Actor and wait for it to finish
run = client.actor("xtracto/gmaps-contact-enrichment").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "query": "dentist",
  "location": "New York, NY",
  "placeUrls": [
    "https://www.google.com/maps/place/Empire+State+Building/@40.7484,-73.9857,17z"
  ]
}' |
apify call xtracto/gmaps-contact-enrichment --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,xtracto/gmaps-contact-enrichment"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/7a4BpnXUdyiCI2Ssq/builds/ndvUFOwHISf45kVVB/openapi.json
