# Chiro Lead Scraper - Maps Phone Email (Unofficial) (`brainy_frostfield/chiro-lead-scraper`) Actor

UNOFFICIAL Google Maps chiropractor lead scraper: doctor-priority emails, PI/auto-accident signals, techniques, offers, phones. Not affiliated with Google. No AI. Follow Apify AUP. Not for unsolicited mass messaging.

- **URL**: https://apify.com/brainy\_frostfield/chiro-lead-scraper.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 place leads

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

## Chiro Lead Scraper – Maps Phone Email (Unofficial)

Build **outreach-ready lists of chiropractic clinics** from Google Maps. Each run can return the Maps listing (name, address, phone, website, rating, hours) plus **public website emails ranked for doctor-style addresses** (`dr@…`, `dr.smith@…`), **auto accident / personal injury (PI) language**, **technique mentions**, **new-patient promotions**, and simple **insurance / online-booking** flags. Leads are tagged **HOT / WARM / COLD** so agencies can prioritize who to call first.

**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.

***

### Who this is for

| Team | Why it helps |
|---|---|
| **Chiro marketing agencies** | Local lists with phone + email + “needs website / needs offer” scoring |
| **PI / injury marketers** | Surface clinics that advertise auto accident, whiplash, or personal injury care |
| **Clinic software & billing vendors** | Segment by online booking, insurance language, multi-location chains |
| **Wellness / supplement / device sellers** | Technique and wellness-care language for niche targeting |
| **Local lead ops** | CRM-ready rows with Maps URL, E.164 phones, and optional MX flags |

This is **not** a medical directory, credentialing product, or license-board verifier. Specialty and technique fields are **public-page text signals** only.

***

### What makes it different from a basic Maps dump

A plain Maps scrape stops at “name + phone + website.” Clinics often hide real contact paths behind forms, publish only `info@`, or put PI language on deep pages. This Actor:

1. Opens each place on **Google Maps** (browser automation — not the Places API).
2. Groups sites by domain and crawls a small set of **high-value public paths** (contact, doctors/team, auto accident, personal injury, new patients, insurance…).
3. Harvests emails from **mailto**, visible text, **obfuscated** patterns (`name [at] clinic [dot] com`), and **Cloudflare-style** encoded attributes when present.
4. **Scores** emails so doctor-pattern locals rank above generic roles; optional **MX** check sorts domains that can receive mail higher.
5. Labels **PI focus**, **techniques**, **new-patient offers**, and booking/insurance hints from deterministic keyword rules.
6. Emits a **lead tier** aimed at agency opportunity (missing site, missing email, unclaimed listing → hotter).

Nothing is invented by AI. If a clinic never publishes an email on public HTML, the Actor will not invent one.

***

### Features (detailed)

#### Maps discovery

- Multi-location × multi-keyword search on Google Maps
- Fields: **business name, category, address, phone, website, rating, review count, hours, coordinates, Maps URL**
- **Unclaimed listing** signal when Maps shows claim/own-this-business style language
- Safe Store defaults: **1 city, 1 term, max 5 places** (raise after residential tests)

#### Email intelligence

- Sources: `mailto:`, page text, obfuscated `at`/`dot` text, Cloudflare `data-cfemail` decode (best-effort)
- Cleaning: strips DOM glue, rejects noreply, image-like addresses, junk hosts (`wixpress`, `sentry`, etc.), long hex tracking locals
- **Doctor-pattern detection**: `dr@`, `dr.…`, `doctor…` (and similar) preferred in sort order
- Role emails (`info@`, `office@`, `appointments@`) kept but scored lower
- Optional free **MX** validation per email domain (domain can receive mail — **not** proof a human answers that mailbox)

#### Clinic intelligence (keyword heuristics)

| Signal group | Examples |
|---|---|
| **Specialties** | Auto Accident, Personal Injury, Workers Comp, Whiplash, Sports Injury, Slip & Fall, Pregnancy / Webster, Wellness Care |
| **Techniques** | Gonstead, Activator, Thompson, Diversified, Graston, Decompression, ART, Webster, Cox, Network Spinal |
| **New-patient offers** | Free Consultation, New Patient Special, Same Day Appointment, X-Ray Included |
| **Ops flags** | Insurance acceptance language, online booking CTAs, “accepting new patients” |

#### Phones & ranking

- Maps phone + website `tel:` / text candidates merged via **libphonenumber**
- Stored as **E.164** + national format when valid
- **HOT / WARM / COLD** + numeric score + human-readable reasons (heuristic only)

***

### How to use

1. Enter **locations** (city, region, or ZIP) — e.g. `Phoenix, AZ`.
2. Enter **search terms** — start with `chiropractors`; use `auto accident chiropractor` or `personal injury chiropractor` when building PI lists.
3. Keep **Max results per query** low first (**5** default; try **10–20** after proxy works).
4. Enable the enrichment toggles you need (emails, specialties, techniques, offers).
5. Set **Proxy → Apify Proxy → RESIDENTIAL** for Maps (strongly recommended).
6. Run the Actor, then export the dataset (CSV/JSON/Excel from the Console).

#### Example input

```json
{
  "locations": ["Phoenix, AZ"],
  "searchTerms": ["chiropractors"],
  "maxPlacesPerQuery": 10,
  "extractEmails": true,
  "extractWebsitePhones": true,
  "detectSpecialties": true,
  "detectTechniques": true,
  "detectNewPatientOffers": true,
  "validateMxRecords": true,
  "maxWebsitePagesPerPlace": 3,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

#### Input field reference

| Field | What it controls |
|---|---|
| Target locations | Where to search on Maps |
| Search terms | Maps query keywords |
| Max results per query | Cap per location × term |
| Max total places | Optional whole-run cap |
| Extract emails | Public email crawl + doctor scoring |
| Extract website phones | Extra phones from clinic sites |
| Detect PI / auto accident specialties | Specialty keyword labels |
| Detect techniques | Technique keyword labels |
| Detect new-patient offers | Promo / consult / same-day signals |
| Validate MX | DNS MX check on email domains |
| Max pages per website | Depth of public page crawl (contact, PI, doctors…) |
| Include places without website | Keep no-site listings (often HOT for agencies) |
| Only with phone / email | Hard filters for CRM hygiene |
| Only doctor-pattern emails | Keep rows with `dr@`-style addresses only |
| Only PI / auto-accident specialists | Keep PI-flagged clinics only |
| Only MX-valid primary email | Keep rows whose top email domain passes MX |
| Proxy for Google Maps | Prefer **RESIDENTIAL** |
| Website proxy (optional) | Separate proxy for site crawl if needed |

***

### Output example

```json
{
  "query": "chiropractors in Phoenix, AZ",
  "businessName": "Desert Valley Chiropractic",
  "category": "Chiropractor",
  "address": "123 Example Rd, Phoenix, AZ 85027",
  "primaryPhone": "+16024391515",
  "primaryPhoneNational": "(602) 439-1515",
  "hasPhone": true,
  "website": "https://example-clinic.com",
  "emails": [
    {
      "email": "dr.lee@example-clinic.com",
      "source": "mailto",
      "confidence": 95,
      "score": 90,
      "isDoctorEmail": true,
      "isRoleEmail": false,
      "mxValid": true
    }
  ],
  "primaryEmail": "dr.lee@example-clinic.com",
  "hasEmail": true,
  "hasDoctorEmail": true,
  "drEmail": "dr.lee@example-clinic.com",
  "mxValidPrimary": true,
  "specialties": ["Auto Accident", "Whiplash"],
  "isPISpecialist": true,
  "isAutoAccident": true,
  "isWorkersComp": false,
  "techniques": ["Diversified", "Decompression"],
  "isDecompression": true,
  "newPatientOffers": ["Free Consultation"],
  "hasFreeConsultation": true,
  "hasNewPatientSpecial": false,
  "hasInsuranceInfo": true,
  "hasOnlineBooking": false,
  "acceptingNewPatients": true,
  "leadScore": 61,
  "leadTier": "WARM",
  "leadReasons": [
    "Doctor-pattern email present — strong direct contact signal",
    "Auto accident / personal injury focus — high-value niche for PI marketing",
    "No online booking signals on crawled pages"
  ],
  "rating": 4.8,
  "reviewsCount": 112,
  "isUnclaimed": false,
  "googleMapsUrl": "https://www.google.com/maps/place/..."
}
```

#### How to read lead tiers

| Tier | Practical reading for outreach teams |
|---|---|
| **HOT** | Often no website, no public email, unclaimed Maps, or several digital gaps — high “fix / SEO / capture” opportunity |
| **WARM** | Reachable clinic with partial maturity (e.g. phone + site but weak offers, no booking, only role email) |
| **COLD** | Stronger digital footprint already — still useful for research, competitive maps, or partner lists |

Tiers are **deterministic heuristics**, not medical quality scores and not purchase intent.

#### Useful boolean shortcuts in the dataset

- `hasDoctorEmail` / `drEmail` — doctor-pattern address present
- `isPISpecialist` / `isAutoAccident` / `isWorkersComp` — PI-oriented language
- `isDecompression` — higher-ticket service language often appears with this technique
- `hasFreeConsultation` / `hasNewPatientSpecial` — promo language
- `hasInsuranceInfo` / `hasOnlineBooking` / `acceptingNewPatients`

***

### Pricing

Billing is **pay per event + platform usage**.

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

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

- Maps discovery runs a full browser. Traffic is heavier than simple HTTP scrapes.
- Use **Apify Proxy → RESIDENTIAL** for stable Maps results.
- Residential is billed **per GB** (plan-dependent; many lower plans land near **$7–$8 / GB** — verify in your Apify plan).
- On small tests, **proxy + compute often exceed event fees**.
- Start with **one city × one keyword × 3–5 places** before scaling.
- Without residential proxy, Maps frequently returns **zero places**.

#### Event fees

| Event name | When it fires | Price |
|---|---|---|
| `placeLead` | Each clinic row saved | **$0.004** ($4.00 / 1,000) |
| `phoneLead` | Lead has ≥1 validated phone | **$0.005** ($5.00 / 1,000) |
| `emailLead` | Lead has ≥1 public email | **$0.005** ($5.00 / 1,000) |

Also expect a tiny **Actor start** fee if enabled in Console. Do **not** keep `apify-default-dataset-item` alongside these custom events, or the same clinic can be billed twice.

#### Event-only cost examples (usage extra)

| Mix | Rough event total |
|---|---|
| 1,000 clinics, phones only | ≈ **$9** + usage |
| 1,000 clinics, phone + email on all | ≈ **$14** + usage |
| 1,000 clinics, phones all, emails on ~40% | ≈ **$11** + usage |

***

### Tips for better lists (and lower waste)

- Prefer residential proxy; scale cities only after a cheap smoke run.
- For PI outreach, mix keywords: `auto accident chiropractor`, `whiplash chiropractor`, `personal injury chiropractor`.
- Increase `maxWebsitePagesPerPlace` to 4–5 when doctor or PI content lives deep in the site.
- Use **only doctor-pattern emails** when you want direct clinician addresses; expect lower volume.
- Use **only PI specialists** after a broad run if you need a PI-only slice.
- Chains (e.g. national brands) may block automated site crawls (403) — Maps phone/address still save; emails may be empty.
- Volume cost scales with **locations × terms × places × pages**; residential GB usually dominates Maps cost.

***

### Compliance & acceptable use

Use only for **lawful** purposes and follow:

- [Apify Acceptable Use Policy](https://docs.apify.com/legal/acceptable-use-policy) — including **no unsolicited mass messaging**, no fraud, no abusive load
- [Apify General Terms and Conditions](https://docs.apify.com/legal/general-terms-and-conditions)
- Google’s Terms and each clinic site’s terms
- Privacy (GDPR/CCPA) and anti-spam (CAN-SPAM/CASL) rules where they apply

This Actor targets **public business pages only**. It does not log into portals, solve CAPTCHAs, open paywalls, fabricate contacts, or send messages for you. Published emails and phones may still be personal data. **Do not spam.** This is not legal advice.

***

### Limitations

- Non-residential IPs are often blocked or empty on Maps.
- Emails inside images, PDFs, widgets, or pure client-side apps may be missed.
- Obfuscation / Cloudflare decoding is best-effort, not universal.
- MX success means “domain has mail servers,” not “this mailbox is monitored.”
- Specialty, technique, and offer labels are **text heuristics**, not board certifications or clinical claims.
- Large national clinic sites may rate-limit or 403 HTML crawls while Maps data still works.
- Google Maps layout and anti-bot behavior change over time.

***

### Troubleshooting

| Symptom | What to try |
|---|---|
| **0 Maps results** | Turn on **RESIDENTIAL** proxy; lower max places; check the run log for consent/block messages |
| **Places but empty emails** | Normal for form-only sites; raise page count; try contact-heavy keywords; check `hasOnlineBooking` |
| **No doctor-pattern emails** | Many clinics only publish `info@` — turn off “only doctor email” or broaden search |
| **Chain site 403 on crawl** | Expected for some brands; rely on Maps phone; optional website proxy |
| **Run is slow or expensive** | Cut locations/terms/max places/pages; residential GB is usually the main cost |
| **Filter empties the dataset** | Disable PI-only / email-only / MX-only / doctor-only for a baseline run |

***

### Support

Open an Issue on this Actor and include:

1. **Run ID**
2. **Input JSON** with secrets removed
3. What you expected vs what you got

Do not send API tokens or proxy passwords.

# Actor input Schema

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

Cities, states, or ZIP codes (e.g. Phoenix, AZ).

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

Maps keywords (chiropractors, auto accident chiropractor, personal injury chiropractor…).

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

Maximum businesses per location × term. Default is small for fast Store tests.

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

Optional run-wide cap. Leave empty/0 for no global cap.

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

Deep-crawl public clinic pages for emails; prioritizes doctor-pattern addresses (dr@…).

## `extractWebsitePhones` (type: `boolean`):

Also collect phones from website tel links and text.

## `detectSpecialties` (type: `boolean`):

Flag auto accident, personal injury, workers comp, whiplash, sports injury, etc.

## `detectTechniques` (type: `boolean`):

Flag Gonstead, Activator, decompression, Webster, ART, etc.

## `detectNewPatientOffers` (type: `boolean`):

Flag free consult, new patient special, same-day appointment, x-ray included.

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

Free DNS check that email domains can receive mail.

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

Pages to check (home, contact, doctors, PI, new patients, insurance…).

## `websiteRequestDelayMs` (type: `integer`):

Polite delay between website page requests.

## `defaultCountryCode` (type: `string`):

ISO country code used when parsing phone numbers.

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

Keep listings even without a website (often HOT for agencies).

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

Only keep leads with at least one validated phone.

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

Only keep leads with at least one public email.

## `onlyDoctorEmail` (type: `boolean`):

Only keep leads with a dr@ / doctor-style email.

## `onlyPISpecialist` (type: `boolean`):

Only keep leads flagged for personal injury or auto accident.

## `onlyMxValidEmail` (type: `boolean`):

Only keep leads whose primary email domain passes MX check.

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

Residential proxy is strongly recommended for Maps.

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

Optional separate proxy for website enrichment. Leave empty to crawl sites directly.

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

How far to scroll the Maps results feed per query.

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

Verbose crawler logs for troubleshooting.

## Actor input object example

```json
{
  "locations": [
    "Phoenix, AZ"
  ],
  "searchTerms": [
    "chiropractors"
  ],
  "maxPlacesPerQuery": 5,
  "extractEmails": true,
  "extractWebsitePhones": true,
  "detectSpecialties": true,
  "detectTechniques": true,
  "detectNewPatientOffers": true,
  "validateMxRecords": true,
  "maxWebsitePagesPerPlace": 3,
  "websiteRequestDelayMs": 800,
  "defaultCountryCode": "US",
  "includeNoWebsite": true,
  "onlyWithPhone": false,
  "onlyWithEmail": false,
  "onlyDoctorEmail": false,
  "onlyPISpecialist": false,
  "onlyMxValidEmail": false,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  },
  "maxScrolls": 10,
  "debugLog": false
}
```

# Actor output Schema

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

Dataset of chiropractor 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": [
        "Phoenix, AZ"
    ],
    "searchTerms": [
        "chiropractors"
    ],
    "maxPlacesPerQuery": 5,
    "maxWebsitePagesPerPlace": 3
};

// Run the Actor and wait for it to finish
const run = await client.actor("brainy_frostfield/chiro-lead-scraper").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": ["Phoenix, AZ"],
    "searchTerms": ["chiropractors"],
    "maxPlacesPerQuery": 5,
    "maxWebsitePagesPerPlace": 3,
}

# Run the Actor and wait for it to finish
run = client.actor("brainy_frostfield/chiro-lead-scraper").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": [
    "Phoenix, AZ"
  ],
  "searchTerms": [
    "chiropractors"
  ],
  "maxPlacesPerQuery": 5,
  "maxWebsitePagesPerPlace": 3
}' |
apify call brainy_frostfield/chiro-lead-scraper --silent --output-dataset

```

## MCP server setup

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

```

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/EfcEfcRzbjgTPEJ7G/builds/dhX2ynOOf4VmSyybI/openapi.json
