# Dentist Lead Scraper – Google Maps + Email Finder (Unofficial) (`brainy_frostfield/dentist-lead-scraper-google-maps-email-finder`) Actor

UNOFFICIAL Google Maps dentist/local lead scraper with public website emails and free MX checks. Not affiliated with Google. No AI language models.

- **URL**: https://apify.com/brainy\_frostfield/dentist-lead-scraper-google-maps-email-finder.md
- **Developed by:** [Viv K](https://apify.com/brainy_frostfield) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 placeleads

This Actor is paid per event and usage. You are charged both the fixed price for specific events and for Apify platform usage.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#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

## Dentist Lead Scraper – Google Maps + Email Finder (Unofficial)

Find **dental and local business leads** from Google Maps, with optional **public website emails** and free **email-domain (MX) checks**.

No AI language models. No Google Places API key required.

> **UNOFFICIAL** — not affiliated with, endorsed by, or sponsored by Google LLC. Google Maps is a trademark of Google LLC.\
> You are solely responsible for complying with Google’s Terms of Service, privacy laws (GDPR/CCPA), anti-spam rules (CAN-SPAM/CASL), website terms, and [Apify’s Acceptable Use Policy](https://docs.apify.com/legal/acceptable-use-policy).\
> **Do not use for unsolicited mass messaging.** Scraping Google Maps may violate Google’s contractual terms even when data is publicly visible.

***

### Features

- Search Google Maps by **location** and **keywords**
- Extract **name, address, phone, website, rating, Maps URL**
- Optional crawl of public website pages for **emails** and **social links**
- Detect **contact forms** when no email is listed
- Free **MX domain check** on found emails
- Simple **niche tag** (e.g. pediatric, orthodontist, general dentist)

Emails are only taken from text that appears on public pages. Nothing is invented by AI.

***

### How to use

1. Add one or more **locations** (city or ZIP).
2. Add **search terms** (e.g. `dentist`).
3. Set **Max results per query** (try **10–20** first).
4. Turn on **Extract emails** / **MX check** if you want website enrichment.
5. Set proxy to **Apify Proxy → RESIDENTIAL** (recommended for Maps).
6. Click **Start**, then download the dataset.

#### Example input

```json
{
  "locations": ["Miami, FL"],
  "searchTerms": ["dentist"],
  "maxPlacesPerQuery": 10,
  "extractEmails": true,
  "validateMxRecords": true,
  "maxWebsitePagesPerPlace": 2,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Input fields

| Field | Description |
|---|---|
| Target locations | Cities, states, or ZIPs |
| Search terms | Maps keywords |
| Max results per query | Cap per location × term |
| Max total places | Optional cap for the whole run |
| Extract emails | Crawl public website pages for emails |
| Validate MX | Free check that the email domain can receive mail |
| Max pages per website | How many site pages to check |
| Proxy for Google Maps | Prefer **residential** |
| Filters | Phone only / email only / MX-valid only / include no website |

***

### Output example

```json
{
  "query": "dentist in Miami, FL",
  "businessName": "Miami Smile Dental",
  "niche": "General dentist",
  "address": "123 Main St, Miami, FL 33101",
  "phone": "+1 305-555-0100",
  "website": "https://miamismiledental.com",
  "rating": 4.8,
  "emails": [
    {
      "email": "info@miamismiledental.com",
      "source": "mailto_contact",
      "confidence": 95,
      "mxValid": true
    }
  ],
  "primaryEmail": "info@miamismiledental.com",
  "mxValidPrimary": true,
  "hasContactForm": false,
  "contactPageUrl": "https://miamismiledental.com/contact",
  "socials": {
    "facebook": "https://www.facebook.com/example",
    "instagram": "https://www.instagram.com/example"
  },
  "googleMapsUrl": "https://www.google.com/maps/place/...",
  "scrapedAt": "2026-08-07T12:00:00.000Z"
}
```

***

### Pricing

**Pay per event** + **platform usage**. You pay **both**:

1. **Event fees** (below) — paid to the Actor developer
2. **Platform usage** — paid to Apify for compute, data transfer, storage, and **proxies**

#### Platform usage & residential proxy (important)

Google Maps runs need **Apify Proxy → RESIDENTIAL**. Residential traffic is billed by Apify **per GB** (rate depends on your plan; often around **$7–$8 / GB** on lower plans).

- Browser/Maps traffic is heavier than simple HTTP scrapes, so even small runs can use tens–hundreds of MB.
- **Proxy + compute usually cost more than the event fees** on short test runs.
- Start with a **small test** (low `maxPlacesPerQuery`) before large city lists.
- Without residential proxy you often get **0 results**, but you still avoid that proxy charge.

#### Event fees

| Event | When charged | Price |
|---|---|---|
| Place lead | Each business saved | **$0.004** ($4.00 per 1,000) |
| Email enrichment | Only if at least one public email is found | **$0.005** ($5.00 per 1,000) |

**Event fee examples** (platform usage — including residential proxy — is **extra**):

| Result mix | Approx. event cost only |
|---|---|
| 1,000 places, no emails | **$4.00** + usage |
| 1,000 places, all with email | **$9.00** + usage |
| 1,000 places, ~40% with email | **~$6.00** + usage |

Total cost scales with locations, search terms, max results, **residential proxy GB**, and compute. Defaults stay small for Store auto-tests and cheap first runs.

***

### Tips

- **Residential proxy** is important for Google Maps. Without it you may get zero results.
- Expect **proxy usage** to be a large share of the run cost; keep max results low while testing.
- More cities × more keywords × higher max results = longer runs and higher **proxy + compute** cost.
- Many clinics only offer a **contact form** (no public email). Check `hasContactForm` and `contactPageUrl`.

***

### Compliance

- Public business information only; no login walls.
- Business emails may still be personal data under GDPR/CCPA in some cases.
- You are responsible for lawful use and outreach (anti-spam and privacy rules).
- Do not use for spam or illegal activity.

***

### Limitations

- Google Maps may block non-residential traffic.
- Emails hidden in images or complex scripts may be missed.
- `mxValid` checks the **domain**, not that a specific mailbox is active.
- Maps does not return unlimited listings per area.
- Maps page layout can change over time.

***

### Troubleshooting

| Problem | What to try |
|---|---|
| 0 results | Use residential proxy; lower max results; check the run log |
| Places but no emails | Common for form-only sites; try more website pages |
| Slow or costly run | Fewer locations/terms; lower max results |
| Empty after filters | Turn off “only with email” / “only MX-valid” for testing |

Support: open an **Issue** on this Actor with your run ID and input (no secrets).

# Actor input Schema

## `locations` (type: `array`):

Cities, states, or ZIP codes (e.g. Miami, FL). Add more cities as needed.

## `searchTerms` (type: `array`):

What to search on Google Maps (e.g. dentist, dental clinic). Works for other local niches too. Raise max results when you need larger lists.

## `maxPlacesPerQuery` (type: `integer`):

Maximum businesses per location × search term. Default is small for fast runs; raise to 20–100 for production lead lists.

## `maxTotalPlaces` (type: `integer`):

Optional limit for the whole run. Leave empty or 0 for no extra limit (only “Max results per query” applies).

## `extractEmails` (type: `boolean`):

Visit each business website (homepage, contact, about) and collect public emails. Form-only sites may have no email.

## `validateMxRecords` (type: `boolean`):

Free DNS check: can this email domain receive mail? Adds mxValid on each email.

## `maxWebsitePagesPerPlace` (type: `integer`):

How many website pages to check for emails (homepage, contact, about, etc.).

## `emailRequestDelayMs` (type: `integer`):

Wait time between website page requests (polite crawling).

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

Keep listings even when Google Maps shows no website.

## `onlyWithPhone` (type: `boolean`):

Only keep leads that have a phone number.

## `onlyWithEmail` (type: `boolean`):

Only keep leads with at least one public website email.

## `onlyMxValid` (type: `boolean`):

Only keep leads where at least one email domain passes the MX check.

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

Residential proxy is strongly recommended. Datacenter IPs are often blocked by Google Maps.

## `websiteProxyConfiguration` (type: `object`):

Optional separate proxy for website email crawling. Leave empty to use the default network.

## `maxScrolls` (type: `integer`):

How many times to scroll the Maps results list to load more businesses.

## `debugLog` (type: `boolean`):

Show more detail in the run log (for troubleshooting).

## Actor input object example

```json
{
  "locations": [
    "Miami, FL"
  ],
  "searchTerms": [
    "dentist"
  ],
  "maxPlacesPerQuery": 5,
  "extractEmails": true,
  "validateMxRecords": true,
  "maxWebsitePagesPerPlace": 2,
  "emailRequestDelayMs": 800,
  "includeNoWebsite": true,
  "onlyWithPhone": false,
  "onlyWithEmail": false,
  "onlyMxValid": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxScrolls": 10,
  "debugLog": false
}
```

# Actor output Schema

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

Dataset of dentist / local business leads.

# 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 = {
    "locations": [
        "Miami, FL"
    ],
    "searchTerms": [
        "dentist"
    ],
    "maxPlacesPerQuery": 5,
    "maxWebsitePagesPerPlace": 2
};

// Run the Actor and wait for it to finish
const run = await client.actor("brainy_frostfield/dentist-lead-scraper-google-maps-email-finder").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 = {
    "locations": ["Miami, FL"],
    "searchTerms": ["dentist"],
    "maxPlacesPerQuery": 5,
    "maxWebsitePagesPerPlace": 2,
}

# Run the Actor and wait for it to finish
run = client.actor("brainy_frostfield/dentist-lead-scraper-google-maps-email-finder").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 '{
  "locations": [
    "Miami, FL"
  ],
  "searchTerms": [
    "dentist"
  ],
  "maxPlacesPerQuery": 5,
  "maxWebsitePagesPerPlace": 2
}' |
apify call brainy_frostfield/dentist-lead-scraper-google-maps-email-finder --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,brainy_frostfield/dentist-lead-scraper-google-maps-email-finder"
        }
    }
}

```

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/KgG2ewgZPdgh88Pe7/builds/epkC4NreHzA7dDw4i/openapi.json
