# Google Maps Lead Enricher | Emails, Phones & Socials (`balladic_porcupine/gmaps-lead-enricher`) Actor

Enrich Google Maps business results with emails, phones and social profiles, plus a lead score that flags weak or missing websites. Chain it after any Maps scraper.

- **URL**: https://apify.com/balladic\_porcupine/gmaps-lead-enricher.md
- **Developed by:** [Daniel Lomonaco](https://apify.com/balladic_porcupine) (community)
- **Categories:** Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 enriched leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

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

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

### What does Google Maps Lead Enricher do?

Google Maps Lead Enricher turns a raw list of local businesses into a **qualified sales pipeline**. Feed it the output of any Google Maps scraper and it visits each business's website to pull out **contact emails, phone numbers, and social profiles**, detects what the site is built with, and scores every business on how weak its web presence is — so you know instantly who is worth a pitch.

A Google Maps scrape tells you a business exists. It does not tell you how to email them, whether their site is broken, or whether they are a prospect. This Actor answers all three.

It is built to **chain directly after any Google Maps scraper** — paste that run's dataset ID and it does the rest. It runs on the Apify platform, so you get API access, scheduling, proxy rotation, monitoring, and integrations with Make, Zapier, Google Sheets, and Slack out of the box.

### Why use Google Maps Lead Enricher?

- **Get contact details Google does not give you.** Google Maps rarely exposes an email address. This Actor harvests them from the business's own website.
- **Find businesses that need what you sell.** Web designers, SEO consultants, and marketing agencies live on one question: which local businesses have a bad or missing website? The lead score answers it directly.
- **Stop wasting outreach on the wrong prospects.** A business with a fast, modern, tracked website is not going to buy a new website. It scores 0 and you skip it.
- **Pay only for what you use.** Billing is per delivered lead. Filter with `minLeadScore` and you are not charged for records you filtered out.

#### Who it is for

| You are | You use the score to |
|---|---|
| A web design agency | Find businesses with no website or a dead one (score 35+) |
| An SEO consultant | Find sites with no analytics and no mobile viewport |
| A hosting/security reseller | Find sites still served over plain HTTP |
| A B2B sales team | Get emails and socials for a whole city's worth of businesses |

### How to use Google Maps Lead Enricher

1. Run any Google Maps scraper (for example `compass/crawler-google-places`) for your city and category.
2. Copy the **dataset ID** from that run.
3. Paste it into this Actor's **Source dataset ID** field and click **Start**.
4. Open the **Output** tab. Sort by `leadScore` and start with the highest.

No upstream scrape? Paste business objects into **Business records**, or a plain list of URLs into **Website URLs**.

### Input

Every field is optional, but you must supply businesses one of three ways: `sourceDatasetId`, `businesses`, or `websites`.

| Field | Type | Default | What it does |
|---|---|---|---|
| `sourceDatasetId` | string | — | Dataset ID from a previous Google Maps scrape. The usual way to chain this Actor. |
| `businesses` | array | `[]` | Business objects to enrich. Extra fields pass through to the output untouched. |
| `websites` | array | `[]` | Plain list of website URLs. |
| `websiteField` | string | `website` | Which field holds the URL in your records. |
| `maxItems` | integer | `1000` | Hard cap on records processed — also caps what you can be charged. |
| `minLeadScore` | integer | `0` | Only deliver businesses at or above this score. You are not charged for filtered records. |
| `requireContact` | boolean | `false` | Skip businesses with no phone and no email. You are not charged for filtered records. |
| `maxConcurrency` | integer | `10` | Parallel site fetches. |
| `requestTimeoutSecs` | integer | `30` | When to give up on a slow site. |
| `useApifyProxy` | boolean | `true` | Route through Apify Proxy. Recommended — many small-business hosts block repeat datacenter traffic. |

```json
{
    "sourceDatasetId": "aBcDeFgHiJkLmNoPq",
    "minLeadScore": 20,
    "requireContact": true,
    "maxItems": 500
}
```

### Output

You can download the dataset in various formats such as JSON, HTML, CSV, or Excel.

```json
{
    "name": "Bob's Auto Repair",
    "phone": "+1 555 0111",
    "address": "42 Main St, Springfield",
    "website": "http://bobsauto.com/",
    "websiteReachable": true,
    "httpStatus": 200,
    "usesHttps": false,
    "emails": ["bob@bobsauto.com"],
    "phones": ["+15550111"],
    "socials": { "facebook": "https://www.facebook.com/bobsauto" },
    "platform": "wix",
    "hasAnalytics": false,
    "analytics": [],
    "mobileFriendly": false,
    "copyrightYear": 2018,
    "leadScore": 65,
    "signals": [
        "no-https",
        "not-mobile-friendly",
        "no-analytics",
        "stale-copyright-2018",
        "diy-builder-wix"
    ]
}
```

#### Data fields

| Field | Description |
|---|---|
| `emails` | Emails from `mailto:` links and page text. Asset filenames and placeholder domains are filtered out. |
| `phones` | Numbers from `tel:` links, plus the number the upstream record already carried. Businesses with no website keep their Google Maps phone — otherwise the strongest leads would arrive with no way to reach them. |
| `socials` | Facebook, Instagram, LinkedIn, X/Twitter, YouTube, TikTok profile URLs. Share buttons are ignored. |
| `platform` | `wordpress`, `shopify`, `wix`, `squarespace`, `webflow`, `godaddy`, or `duda`. |
| `analytics` | Detected tracking: `ga4`, `gtm`, `meta-pixel`, `hotjar`, `plausible`. |
| `mobileFriendly` | Whether the page declares a viewport meta tag. |
| `copyrightYear` | Most recent copyright year found in the page. |
| `websiteReachable` | Whether the site answered with a status below 400. |
| `leadScore` | 0–100. **Higher means a weaker web presence and a better prospect.** |
| `signals` | The specific reasons behind the score. |
| `leadType` | `no-website`, `dead-site`, `weak-site` or `healthy`. Use this, not the score, to isolate a category. |
| `contactable` | Whether the business has at least one phone or email. |

#### How the lead score works

| Signal | Points | Meaning |
|---|---|---|
| `no-website` | 40 | No website at all — the strongest buying signal. Short-circuits. |
| `site-unreachable` | 35 | Domain dead or refusing connections. Short-circuits. |
| `no-https` | 20 | Still on plain HTTP. |
| `not-mobile-friendly` | 20 | No viewport meta tag. |
| `no-analytics` | 10 | Nobody is measuring anything. |
| `stale-copyright-YYYY` | 10 | Copyright two or more years behind. |
| `diy-builder-*` | 5 | Built on Wix, GoDaddy, Squarespace, or Duda. |
| `no-public-email` | 5 | No reachable email published. |

A healthy, modern, tracked site scores **0** — deliberately. There is nothing to sell it.

### What a real run looks like

300 auto repair shops in Phoenix, Arizona, enriched on 2026-08-22:

| | |
|---|---|
| businesses checked | 297 |
| scored 20 or above | 143 |
| **no website at all** | **106** (36%) |
| website dead or unreachable | 14 (5%) |
| still HTTP-only | 124 (42%) |
| not mobile-friendly | 131 (44%) |
| no analytics installed | 189 (64%) |
| **no email findable anywhere** | **213** (72%) |
| healthy, scored 0 | 38 |

Two things worth knowing before you plan outreach. Most local businesses in a trade like this
**cannot be reached by email at all** — 72% here had no public address — so the deliverable is
usually a call list; 283 of the 297 came back with a phone number. And web presence tracks how
busy a business is (review count against lead score came out at r = -0.46), so the businesses
that need the most help tend to be the smallest.

### How much does it cost?

**$0.005 per delivered lead — $5.00 per 1,000.** One charge per lead actually delivered to your dataset. Records filtered out by `minLeadScore` are never charged, so you pay only for businesses that cleared the bar you set. `maxItems` gives you a hard ceiling on any single run.

Businesses with no website cost the same as any other lead but require no HTTP request, so runs over a low-website-penetration category finish fast.

### Tips

- **To get only businesses with no website, filter on `leadType`, not on the score.** The score is a sum, so a business with a bad site can stack signals and outrank a business with no site at all — on a real 297-business run, `minLeadScore: 35` returned 130 records where 120 were the no-website-or-dead set, with ten weak sites mixed in. Sort or filter the output by `leadType` (`no-website`, `dead-site`, `weak-site`) and you get the exact category you asked for.
- **Turn on `requireContact` if you intend to actually contact the list.** It drops businesses with no phone and no email — you are not charged for them. On the same run that was 14 of 297.
- **Use `minLeadScore: 0` for contact harvesting.** If you want emails for everyone rather than prospect qualification, take the whole set.
- **Lower `maxConcurrency` for small towns.** Many local businesses share one small host, and 10 parallel requests can look like a spike.
- **Keep Apify Proxy on** for anything above a few dozen businesses.

### FAQ

**Does this scrape Google Maps itself?**
No. It enriches business records you already have. Pair it with a dedicated Maps scraper — that separation keeps this Actor fast and cheap.

**Why is a business with a great website scored 0?**
Because it is not a prospect. The score measures sales opportunity, not site quality.

**Where do the emails come from?**
Only from the business's own public website — `mailto:` links and visible page text. Nothing behind a login, and no third-party data broker.

**How do I find local businesses that need a website?**
Run a Google Maps scrape for the category and city you sell into, pass the dataset to this Actor, and set `minLeadScore: 35`. What comes back is the set of businesses with no website at all or one that no longer loads — a ranked list of businesses that need a website, with a contact address attached to each.

**Can I get a list of businesses without websites in my city?**
Yes, that is the highest-scoring bucket. A missing website scores 40 and an unreachable one scores 35, so sorting the output by `leadScore` descending puts them at the top. Set `minLeadScore: 40` to deliver only businesses with no website.

**Is this a Google Maps email extractor?**
It extracts emails, but not from Google Maps — Maps almost never publishes one. It visits the business's own website and harvests public `mailto:` links and visible contact addresses, which is where a small business actually puts its email. Phones and social profiles come out of the same pass.

**How do I build a lead list for a web design or SEO agency?**
That is the case this Actor was designed around. Scrape a category, enrich it here, and the score sorts the market by how badly each business needs your service: no site, dead site, plain HTTP, no mobile viewport, no analytics, a copyright notice years out of date. You are not charged for the businesses that come back healthy, because those were never prospects.

**How do I find businesses with outdated or broken websites?**
Set `minLeadScore: 20`, which catches any meaningful weakness rather than only missing sites. The `signals` field on each record tells you exactly what was wrong — stale copyright year, no HTTPS, no mobile viewport, DIY site builder, unreachable host — so you can open the conversation with a specific observation instead of a generic pitch.

**Is scraping legal?**
This Actor only requests publicly available pages that businesses publish for customers to read, and respects timeouts and retry limits. You are responsible for how you use the output, including compliance with GDPR, CAN-SPAM, and local marketing law when contacting businesses. Collect and store personal data only where you have a lawful basis.

**Something is wrong or missing.**
Open the **Issues** tab on the Actor page. Custom field extraction and additional detectors can be added on request.

# Actor input Schema

## `sourceDatasetId` (type: `string`):

Dataset from a previous Google Maps scraper run. Every item is enriched. This is the usual way to chain this Actor after a Maps scrape.

## `businesses` (type: `array`):

Business objects to enrich, each with at least a website (or none, which scores as the strongest lead). Extra fields are passed through to the output untouched.

## `websites` (type: `array`):

Plain list of business website URLs, when you have no other fields.

## `websiteField` (type: `string`):

Which field holds the website URL in your input records. Google Maps scrapers commonly use 'website'.

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

Hard cap on how many records are processed. Also caps what you can be charged, since billing is per delivered lead.

## `minLeadScore` (type: `integer`):

Only deliver businesses scoring at or above this. 0 delivers everything. Try 40 for businesses with no website or a dead one; 20 for any meaningful weakness. You are not charged for filtered-out records.

## `requireContact` (type: `boolean`):

Skip any business with no phone number and no email address. A lead you cannot reach is not a prospect, and you are never charged for the records this filters out. Recommended when you plan to actually contact the list.

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

Parallel site fetches. Lower this if you are hitting small hosts that rate-limit.

## `requestTimeoutSecs` (type: `integer`):

How long to wait for a business site before treating it as unreachable.

## `useApifyProxy` (type: `boolean`):

Route requests through Apify Proxy. Recommended: many small business sites block datacenter traffic from a single IP.

## Actor input object example

```json
{
  "sourceDatasetId": "aBcDeFgHiJkLmNoPq",
  "businesses": [
    {
      "name": "Example Plumbing",
      "website": "https://example.com",
      "phone": "+1 555 0100"
    }
  ],
  "websites": [],
  "websiteField": "website",
  "maxItems": 1000,
  "minLeadScore": 0,
  "requireContact": false,
  "maxConcurrency": 10,
  "requestTimeoutSecs": 30,
  "useApifyProxy": true
}
```

# Actor output Schema

## `results` (type: `string`):

No description

# 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 = {
    "businesses": [
        {
            "name": "Example Plumbing",
            "website": "https://example.com",
            "phone": "+1 555 0100"
        }
    ],
    "websites": []
};

// Run the Actor and wait for it to finish
const run = await client.actor("balladic_porcupine/gmaps-lead-enricher").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 = {
    "businesses": [{
            "name": "Example Plumbing",
            "website": "https://example.com",
            "phone": "+1 555 0100",
        }],
    "websites": [],
}

# Run the Actor and wait for it to finish
run = client.actor("balladic_porcupine/gmaps-lead-enricher").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 '{
  "businesses": [
    {
      "name": "Example Plumbing",
      "website": "https://example.com",
      "phone": "+1 555 0100"
    }
  ],
  "websites": []
}' |
apify call balladic_porcupine/gmaps-lead-enricher --silent --output-dataset

```

## MCP server setup

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

```

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/JbdutIEw1YCEyO8fp/builds/ur1MvAKgRLAAC05Vy/openapi.json
