LeadPuppet
Open-source B2B lead discovery — self-hosted.
Node ≥ 20 · Google Places API · OpenAI (optional for discovery)
From a category and a city to a contact list.
- 01
Discover
Queries the Google Places API by category × city (or an ICP profile), paginates, and dedupes by website domain.
- 02
Extract with regex
Visits /contact, /contact-us, /about and the home page. A same-domain email found by regex costs zero tokens.
- 03
Fall back to AI
Only when regex comes up empty, cleaned HTML goes to gpt-4o-mini for name, role, email and a confidence score.
- 04
Export
Enriched leads land in a JSON file you can turn into a CSV and drop into your CRM or outreach tool.
One search, start to finish.
A replay of a real-shaped run: discovery on Google Places, then contact extraction — regex first, AI only when the HTML has no usable email.
- Discover
- Regex
- AI fallback
- Export
- SSunbelt Roofing Co.Phoenix · roofing & hvac
- CCopper State HVACPhoenix · roofing & hvac
- MMesa Ridge RoofersPhoenix · roofing & hvac
- LLone Oak RoofingDallas · roofing & hvac
- TTrinity Air & HeatDallas · roofing & hvac
- BBluebonnet ComfortDallas · roofing & hvac
Simulated run — every business, domain and contact here is fictional.
What you get back.
One record per business with a website. confidence is high for an explicit email,medium when found by regex or inferred, low when the email's domain doesn't match the site.
{
"generatedAt": "2026-09-17T19:04:12.381Z",
"count": 2,
"leads": [
{
"name": "Phoenix Roofing Pros",
"website": "https://www.phoenixroofingpros.com",
"phone": "(602) 555-0142",
"address": "1234 E Camelback Rd, Phoenix, AZ 85014, USA",
"rating": 4.8,
"reviewCount": 152,
"sourceQuery": "roofing in phoenix",
"contactName": "Maria Alvarez",
"contactRole": "Owner",
"contactEmail": "maria@phoenixroofingpros.com",
"confidence": "high"
},
{
"name": "Desert Sky Roofing & Repair",
"website": "https://desertskyroofing.com",
"phone": "(602) 555-0199",
"address": "88 N 7th Ave, Phoenix, AZ 85007, USA",
"rating": 4.5,
"reviewCount": 89,
"sourceQuery": "roofing in phoenix",
"contactName": null,
"contactRole": null,
"contactEmail": "info@desertskyroofing.com",
"confidence": "medium"
}
]
}Sample built from the repo's fixture data — businesses and contacts are fictional.
Run it locally in a minute.
# Clone the repo
git clone https://github.com/ginona/leadpuppet.git# Install dependencies
cd leadpuppet && pnpm install# Set up your API keys
pnpm leadpuppet init# Find leads
pnpm leadpuppet find "roofing,drywall" "houston,dallas"For bigger runs there's an advanced mode: discover and enrich as separate steps, with goal-driven ICP profiles. See the README.
Need API keys first? See the setup guide
Why it exists, how the regex-then-LLM layering keeps costs down, and what I learned building it.
Want to contact me?
Questions, ideas, or want help running it for your team — drop me a line.
contact@gino.run