# Google Maps Leads Website Audit & Pitch Generator (`latentlark/local-lead-audit`) Actor

Audit local business websites from a Google Maps dataset or URL list. Get a 0-100 problemScore and A-F grade, the owner's name, emails, phones and socials, plus a personalised cold email opener. For web design, SEO and marketing agencies. No AI cost.

- **URL**: https://apify.com/latentlark/local-lead-audit.md
- **Developed by:** [LatentLark](https://apify.com/latentlark) (community)
- **Categories:** Lead generation, SEO tools, Marketing
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 business auditeds

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?

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 Leads Website Audit & Pitch Generator

Turn a list of local businesses into **qualified, ready-to-pitch leads**. Give it a Google Maps scraper dataset or a list of websites. For every business it audits the website, scores how much help it needs (0–100), finds the owner's name, emails, phones and social profiles, and writes a personalised **pitch angle and cold-email opener** from what it found.

Built for **web designers, SEO and marketing agencies, freelancers, lead-gen sellers and anyone selling services to local businesses** (plumbers, dentists, salons, restaurants, contractors, clinics…).

- 🔎 **Website audit**: HTTPS/SSL, title & meta description, mobile viewport, H1, outdated © year, load time & page size, Google Analytics/GTM, Meta/TikTok/Google Ads pixels, online booking, live chat, contact forms, LocalBusiness schema, site builder (WordPress, Wix, Squarespace, GoDaddy, Shopify, Duda, Webflow…).
- 🎯 **problemScore + grade (A–F)**: sort by score, and the businesses that need you most come first. A lead with **no website** or a **broken site** scores 100.
- 👤 **Owner & contacts**: owner/founder name from the about, team and contact pages; emails (generic vs personal, with an MX check), phones, Facebook/Instagram/LinkedIn/Yelp/… links.
- ✉️ **Pitch angle + email opener**: template-based (no AI cost, no hallucinations), built from the lead's biggest real issue, e.g. *"Hi Dr. Smith, I couldn't find a way to book or send a request on …"*.
- ⭐ Keeps **Google Maps rating and review count** and flags low ratings or few reviews.
- 🔐 **Limited permissions:** it can read only the one dataset you pick, nothing else in your account.
- 💸 **Pay only for results**: blocked sites, errors and duplicates are free.

![Results: one row per business with problemScore, grade, top issue, owner name, email and a personalised cold email opener](https://api.apify.com/v2/key-value-stores/Tv899H9p8Kim9T8M2/records/local-lead-audit-results-table.png?signature=1uqtyt8a6vEdNoheocJts)

### Quick start (first run in about 1 minute)

**Try it now:** the form is prefilled with 2 websites. Click **Start** and you get 2 audited leads in under a minute for about $0.02.

**With your own leads:**

1. Run any Google Maps scraper (for example `compass/crawler-google-places`) for e.g. *"plumber in Austin, TX"*.
2. In **Google Maps dataset** here, pick that run's dataset (or paste its dataset ID) and click **Start**.
3. Open the **Overview** tab: leads with the highest problemScore first, with owner name, email and a ready-made opener.

No Maps data? Paste website URLs into **Websites** instead (`Joe's Plumbing | joesplumbing.com` lets you add the business name too).

![Walkthrough: the prefilled form, click Start, then the audited leads in the Overview tab](https://api.apify.com/v2/key-value-stores/Tv899H9p8Kim9T8M2/records/local-lead-audit-walkthrough.gif?signature=TcPnXv4nPkWKRcDCx3hA)

### What you can do with it

- **Web design / SEO agencies:** find local businesses with outdated, slow, insecure or non-mobile sites, or with no website at all, and pitch each one with a specific, verifiable reason.
- **Marketing / ads freelancers:** find businesses without analytics or ad pixels ("you can't retarget your visitors").
- **Booking / chat / review software sellers:** filter on `checks.bookingTools`, `checks.chatWidgets` and Maps rating.
- **Lead-gen sellers:** enrich a Maps export with owner names, emails and a qualification score before you sell or import it.
- **Monitoring:** schedule it with `onlyNew` to audit only businesses you haven't seen before.

### Input

| Field | Type | Description |
|---|---|---|
| `mapsDatasetId` | dataset | Dataset (ID or name) from a Google Maps scraper run (compass/crawler-google-places format and similar). Uses title, website, phone, rating, review count, category, city, emails. |
| `websites` | array | Website URLs or domains. Optional `Business Name \| domain.com` format. Can be combined with the dataset. |
| `businessNames` | array | Optional names matching `websites` by position. |
| `maxBusinesses` | integer | Maximum businesses to audit (default 100). |
| `includePitch` | boolean | Generate `pitchAngle` and `emailOpener` (default true). |
| `includeNoWebsite` | boolean | Also output Maps businesses with no website (no-website leads are great web-design leads). Default true. |
| `onlyNew` | boolean | Skip businesses audited in earlier runs (stored per `monitorKey`). |
| `monitorKey` | string | Name of the onlyNew memory, e.g. `austin-plumbers`. |
| `maxSubpages` | integer | About/team/contact pages to crawl per site to find owner and email (0–5, default 3). |
| `checkMx` | boolean | Check that the email domain accepts mail (default true). |
| `maxConcurrency` | integer | Sites audited in parallel (default 10). |
| `proxyFallback` | boolean | Retry blocked sites once through Apify **datacenter** proxy (default true). Residential is never used unless you set it. |

#### Example inputs

From a Google Maps scraper dataset:

```json
{
  "mapsDatasetId": "YOUR_MAPS_DATASET_ID",
  "maxBusinesses": 200,
  "onlyNew": true,
  "monitorKey": "austin-plumbers"
}
```

From a list of websites:

```json
{
  "websites": ["Joe's Pizza | joespizzanyc.com", "https://www.example-dental.com"],
  "includePitch": true
}
```

### Output

One row per business. Example (shortened):

```json
{
  "businessName": "Example Family Dentistry",
  "website": "https://www.examplefamilydentistry.com/",
  "domain": "examplefamilydentistry.com",
  "category": "Dentist",
  "city": "Austin",
  "rating": 4.9,
  "reviewCount": 120,
  "status": "ok",
  "problemScore": 26,
  "grade": "B",
  "topIssues": ["No analytics installed", "No retargeting/ad pixel", "No booking, contact form or chat"],
  "issueCodes": ["NO_ANALYTICS", "NO_AD_PIXEL", "NO_LEAD_CAPTURE", "NO_LOCAL_SCHEMA"],
  "ownerName": "Dr. Jane Smith",
  "ownerTitle": "Owner and Lead Dentist",
  "primaryEmail": "info@…",
  "emailType": "generic",
  "emailMxValid": true,
  "phones": ["(512) …"],
  "socials": {"facebook": "https://facebook.com/…", "instagram": "https://instagram.com/…"},
  "checks": {"https": true, "sslValid": true, "builder": null, "analytics": [], "adPixels": [], "bookingTools": [], "chatWidgets": [], "hasContactForm": false, "copyrightYear": 2026, "responseTimeMs": 467, "pageSizeKb": 95},
  "pitchAngle": "Add online booking / a lead form",
  "emailOpener": "Hi Dr. Smith, I couldn't find a way to book or send a request on examplefamilydentistry.com. Visitors who don't want to phone have no way to reach you."
}
```

`status` is one of `ok`, `noWebsite`, `down`, `httpError`, `blocked` (bot protection, not charged), `error` (not charged).

The run also saves a **SUMMARY** record in the key-value store: counts by status and grade, average problemScore and charged events.

#### problemScore

Points per issue, capped at 100: no website / site down 100, broken site 85, no HTTPS or invalid SSL 20, not mobile-friendly 18, very slow (>6 s) 14, no booking/form/chat 10, low Google rating (<4.0) 9, no analytics 8, slow (>3 s) 8, outdated © year 7, missing title 7, few reviews 6, no http→https redirect 6, no ad pixel 5, missing meta description 5, heavy page 4, and smaller items (no H1, no LocalBusiness schema, no social links, no online booking) 2–3. Grades: A ≤20, B ≤40, C ≤60, D ≤80, F >80.

### Pricing

Pay per event, no subscription:

| Event | Price |
|---|---|
| Actor start | $0.005 per run |
| Business audited | $0.004 |
| Contact found (owner name or website email) | $0.002 extra |

Example: 500 businesses with contacts found for 300 = $0.005 + 500 × $0.004 + 300 × $0.002 = **$2.61**.
Blocked sites, errors, duplicates, permanently closed listings and already-audited businesses (onlyNew) are **free**. Set a max charge per run in Apify and the Actor stops cleanly at the limit.

### Limitations

- Checks the homepage plus up to 5 about, team or contact pages. It isn't a full SEO crawl or Lighthouse test. Load time is measured server-side.
- Sites behind strong bot protection (Cloudflare challenges, AWS WAF) are marked `blocked` and not charged.
- Owner names are found from page text and structured data. Small sites often don't name the owner, so `ownerName` may be empty. `contactName` then holds the best other named person when there is one.
- The Actor finds only emails that are published on the website. It doesn't guess or verify mailboxes; the MX check only confirms the domain accepts mail.
- Pitch texts are templates. Review them before sending, and follow anti-spam and privacy rules (CAN-SPAM, GDPR) in your country.
- This Actor doesn't scrape Google Maps itself. Chain it after a Maps scraper dataset.

### FAQ

**Which Google Maps scraper works?** Any dataset with `title`, `website`, `phone` and optional `totalScore`/`reviewsCount`/`categoryName`/`city`, the format of compass/crawler-google-places. Common alternative field names (`name`, `rating`, `reviews`, `site`) are also recognised.

**Does it use AI?** No. Openers are deterministic templates filled with real findings, so there's no AI cost and nothing is invented.

**Can I run it on a schedule?** Yes. Use `onlyNew` with a `monitorKey` to pay only for new businesses.

**Can I send results to Google Sheets?** Yes, with our Google Sheets Export Actor below.

### More from LatentLark

- [Google Sheets Export & Import (No OAuth)](https://apify.com/latentlark/sheets-export): send these leads to a Google Sheet on a schedule without Google login or expiring tokens.
- [Competitor Ads Monitor](https://apify.com/latentlark/competitor-ads-monitor): see which local competitors run Google or LinkedIn ads, a great add-on for the "no ad pixel" pitch.
- [Career Page Jobs API](https://apify.com/latentlark/ats-career-jobs): every open job from Workday, Personio, Greenhouse, Lever, Ashby and 3 more ATS as clean JSON, with only-new-jobs monitoring.
- [Telegram Channel Monitor](https://apify.com/latentlark/telegram-channel-monitor): public Telegram channel posts as JSON, with only-new-post monitoring.

# Actor input Schema

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

Business websites or domains, one per line (e.g. joespizzanyc.com). You can also write "Business Name | domain.com". Use this or a Google Maps dataset below (or both).

## `mapsDatasetId` (type: `string`):

Pick (or paste the ID of) the dataset from a Google Maps scraper run, e.g. compass/crawler-google-places. Uses title, website, phone, rating, review count, category, city and emails. Read-only access to just this dataset.

## `businessNames` (type: `array`):

Optional names matching the Websites list line by line. Used in the pitch and email opener.

## `maxBusinesses` (type: `integer`):

Stop after auditing this many businesses (after de-duplication by domain).

## `includePitch` (type: `boolean`):

Add a pitchAngle and a one-line emailOpener for the biggest problem found. Template-based, no AI cost.

## `includeNoWebsite` (type: `boolean`):

Output Google Maps rows that have no website (or only a Facebook page) as top-priority 'no website' leads. They count as an audit.

## `onlyNew` (type: `boolean`):

Remember audited domains between runs and skip them. Handy for scheduled runs on a growing lead list. Skipped businesses are not charged.

## `monitorKey` (type: `string`):

Name of the memory used by 'only new'. Use different keys for different campaigns. Defaults to the Maps dataset ID or 'default'.

## `maxSubpages` (type: `integer`):

How many About / Team / Contact pages to open per site to find the owner and emails. 0 = homepage only (faster).

## `checkMx` (type: `boolean`):

Light DNS check that the primary email's domain accepts mail. Not a full mailbox verification.

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

Websites audited in parallel.

## `proxyFallback` (type: `boolean`):

If a site blocks plain requests, retry once through Apify datacenter proxy. Never uses residential proxy unless you set it below.

## `proxyConfiguration` (type: `object`):

Optional. Leave empty for direct requests (recommended). If set, all requests use this proxy.

## Actor input object example

```json
{
  "websites": [
    "https://www.joespizzanyc.com",
    "https://www.levainbakery.com"
  ],
  "maxBusinesses": 20,
  "includePitch": true,
  "includeNoWebsite": true,
  "onlyNew": false,
  "maxSubpages": 3,
  "checkMx": true,
  "maxConcurrency": 10,
  "proxyFallback": true
}
```

# Actor output Schema

## `overview` (type: `string`):

Score, grade, top issues, owner, email, phone, pitch angle and email opener per business.

## `summary` (type: `string`):

Counts by status and grade, charged events and requests.

# 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 = {
    "websites": [
        "https://www.joespizzanyc.com",
        "https://www.levainbakery.com"
    ],
    "maxBusinesses": 20
};

// Run the Actor and wait for it to finish
const run = await client.actor("latentlark/local-lead-audit").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 = {
    "websites": [
        "https://www.joespizzanyc.com",
        "https://www.levainbakery.com",
    ],
    "maxBusinesses": 20,
}

# Run the Actor and wait for it to finish
run = client.actor("latentlark/local-lead-audit").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 '{
  "websites": [
    "https://www.joespizzanyc.com",
    "https://www.levainbakery.com"
  ],
  "maxBusinesses": 20
}' |
apify call latentlark/local-lead-audit --silent --output-dataset

```

## MCP server setup

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

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/TpefHTd6MyZKdcwOS/builds/iMv8fXCLF68rzfwg6/openapi.json
