# Local Enrich

> Local business owner enrichment pipeline — Google Maps scraping, owner finding, email discovery. 97% cheaper than Clay. USE WHEN user says "local-enrich" OR "local enrich" OR "find business owners" OR "maps scraping" OR "scrape maps" OR "local business leads" OR "owner enrichment" OR "find owners for" OR wants to build a lead list from Google Maps searches.

- Skill: `revgrowth1/local-enrich` (Agent Skill, multi-file: 22 files)
- Install (CLI): `npx skillmds@latest add revgrowth1/local-enrich`
- Raw SKILL.md: https://api.skillmd.com/api/skills/revgrowth1/local-enrich/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: Revgrowth1 (https://skillmd.com/u/revgrowth1)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/revgrowth1/local-enrich

---


# Local Business Owner Enrichment Pipeline

End-to-end pipeline: Google Maps search -> Owner identification -> Email discovery -> Campaign-ready segmentation. 97% cheaper than Clay with 81% email coverage. Includes lead scoring, MX host classification, and cost tracking.

Docs: https://{YOUR_DOCS_URL}/

## 4-Stage Pipeline

|Stage|Script|What It Does|
|---|---|---|
|1|`1_scrape_maps.py`|Scrape Google Maps via Serper API for business listings|
|2|`2_find_owners.py`|5-layer waterfall to identify business owners (LinkedIn, BlitzAPI, web scraping, Facebook, state registries, LLM)|
|3|`3_find_emails.py`|Parallel email waterfalls for personal + generic emails (Prospeo, IcyPeas, BlitzAPI, LeadMagic verification)|
|4|`4_prepare_campaigns.py`|Score leads (A/B/C tiers), classify email hosts via MX, segment into campaign-ready CSVs|

## Execution

```bash
# Full pipeline — query + location
python3 ./run_pipeline.py --query "plumbers" --location "Dallas, TX"

# Bulk mode — query file (one per line, or query|location)
python3 ./run_pipeline.py --query-file queries.txt

# Nationwide ZIP code scraping (one query per US ZIP code)
python3 ./run_pipeline.py --query "Juice bar" --zip-file ./data/us_zipcodes.txt --max-results 20

# Resume interrupted ZIP scrape
python3 ./run_pipeline.py --query "Juice bar" --zip-file ./data/us_zipcodes.txt --resume

# Start from existing CSV (skip Maps scraping)
python3 ./run_pipeline.py --input existing.csv

# Run a single step
python3 ./run_pipeline.py --query "plumbers" --location "Dallas, TX" --step 1

# Resume interrupted pipeline
python3 ./run_pipeline.py --query "plumbers" --location "Dallas, TX" --resume
```

## Key Options

|Flag|Default|Purpose|
|---|---|---|
|`--zip-file`|-|ZIP code mode: text file with one ZIP per line, generates one query per ZIP|
|`--max-results`|100|Max Maps results per query (use 20 for ZIP mode)|
|`--max-workers`|1|Thread pool size for steps 2-3|
|`--step 1\|2\|3\|4`|all|Run only a specific step|
|`--resume`|off|Resume from checkpoints (includes ZIP scrape checkpoint)|
|`--skip-verify`|off|Skip LeadMagic email verification|
|`--cost-file`|auto|Path to cost tracking JSON (auto-created in output dir)|
|`-o, --output-dir`|auto|Custom output directory|

## Output

Creates a dated directory under `./output/` containing:

|File|Contents|
|---|---|
|`businesses.csv`|Step 1 - scraped business listings|
|`businesses_with_owners.csv`|Step 2 - listings + identified owners|
|`businesses_enriched.csv`|Step 3 - full enrichment with emails (+ `lead_score`, `lead_tier` after Step 4)|
|`campaigns/`|Step 4 - campaign-ready CSVs segmented by email host x lead tier|
|`campaigns/segment_summary.json`|Step 4 - segment counts, tier/host distributions, sendable rate|
|`cost_tracking.json`|API call counts and costs across all steps|
|`stats.json`|Pipeline statistics|

## Step 4: Campaign Preparation

Step 4 transforms enriched leads into campaign-ready CSVs. Three operations:

1. **Lead Scoring** (0-100, A/B/C tiers): Owner confidence (40pts), email quality (35pts), data completeness (15pts), business signals (10pts). Tiers: A >= 70, B >= 40, C < 40.
2. **MX Classification**: Resolves email domains via DNS MX records to classify as google/microsoft/other. Threaded batch resolution with domain-level caching.
3. **Segmentation**: Splits leads by email_host x lead_tier into campaign CSVs (e.g. `google_tier_a.csv`, `microsoft_tier_b.csv`). Small segments merge into `{host}_mixed` or `other_mixed`. Generic-only leads go to `generic_email.csv`. Unsendable leads go to `unsendable.csv`.

Campaign CSV columns match CampaignPlatform format: `email`, `first_name`, `last_name`, `company_name`, `job_title`, `company_domain`, `phone`, `address`, `category`, `lead_score`, `lead_tier`.

## API Keys

Resolved in order: CLI flag -> env var -> `~/.env`.

|Key|Used In|
|---|---|
|`SERPER_API_KEY`|Steps 1, 2|
|`BLITZ_API_KEY`|Steps 2, 3|
|`LLM_ENDPOINT`, `LLM_API_KEY`, `LLM_MODEL`|Step 2 (owner extraction via LLM)|
|`PROSPEO_API_KEY`|Step 3|
|`ICYPEAS_API_KEY`, `ICYPEAS_API_SECRET`|Step 3|
|`LEADMAGIC_API_KEY`|Step 3 (email verification)|

## Important Notes

- **ALWAYS verify emails** before sending campaigns (don't use `--skip-verify` for production lists)
- **ZIP mode** uses `data/us_zipcodes.txt` (40,781 US ZIP codes) for nationwide coverage. Use `--max-results 20` (most ZIPs have few results per vertical). Checkpoints every 100 queries, progress every 500.
- Step 2 uses LLM analysis — configure `LLM_ENDPOINT` for owner extraction from web pages
- Checkpoints save progress per step — safe to interrupt and resume
- The `texas_queries.txt` file contains example queries for trade service verticals

Script: `./run_pipeline.py`

