Whatever you sell to local businesses, your AI agent finds the ones that need it.
Demo · Quick start · Agents · Niches · How it works · Providers · Integrations · Help test
![]() Claude Code |
![]() Codex |
![]() Antigravity |
![]() Grok |
![]() Hermes |
![]() OpenClaw |
JSON-first CLI · eight agent skills · five specialist agents · optional MCP server.
827 businesses in Greenwich Village, every website checked live. Red = a verified reason to call.
The demo shows an agent turning a plain-English request into a local-business search, checking the leads, and filling the live map with evidence-backed priorities.
You need Python 3.11+ and uv. You do not need to clone this repository.
Run it on demand:
alias leadshoot='uvx --from "leadshoot[all] @ git+https://github.com/moasq/leadshoot" leadshoot'Or install a permanent command:
uv tool install "leadshoot[all] @ git+https://github.com/moasq/leadshoot"The first uvx run takes a few seconds while uv builds an isolated
environment. Later runs use the cache.
Do this before choosing gaps yourself:
leadshoot niche "I sell bookkeeping services to independent cafés"The deterministic classifier returns one of:
gaps: find businesses with a problem your service fixes.fit: find healthy businesses likely to buy a product, supply, or B2B service.clarify: answer the listed question before creating the search.
For a gap service:
leadshoot icp new \
--name austin-dentists-web \
--area "Austin, Texas, United States" \
--categories dentist \
--service website_design \
--mode gaps \
--provider osm \
--jsonFor a product, supply, or B2B service:
leadshoot icp new \
--name cambridge-cafe-bookkeeping \
--area "Cambridge, Massachusetts, United States" \
--categories cafe \
--service general \
--mode fit \
--provider osm \
--jsonUse an unambiguous place name including state/province and country. If a search returns businesses from the wrong region, correct the area and start a new search.
leadshoot find --icp cambridge-cafe-bookkeeping --limit 25 --jsonfind performs discovery, checks websites concurrently, classifies the
available evidence, and creates a research queue. Large areas can take one to
three minutes.
The live map opens automatically and repaints while results arrive. Reopen it
with leadshoot ui; stop its background server with leadshoot ui --stop.
On a headless machine, add --no-open or set LEADSHOOT_NO_OPEN=1.
leadshoot research-queue --search latest --jsonEach queue item is a small, independent job: verify an aggregate review rating, recent social activity, business age, or another fact that affects the decision. Unknown data remains unknown—it is never silently treated as a problem.
Record only public business facts and aggregates:
leadshoot review add <LEAD_ID> \
--source google --rating 4.6 --count 128 \
--url "https://public-profile.example" \
--icp cambridge-cafe-bookkeeping --json
leadshoot signal add <LEAD_ID> \
--key business.founded_year --source website --value 2014 \
--url "https://business.example/about" \
--icp cambridge-cafe-bookkeeping --jsonThe lead is reclassified after every verified signal.
leadshoot leads --search latest --priority high --json
leadshoot show <LEAD_ID> --json
leadshoot mark <LEAD_ID> --stage contacted --note "Called 23 July" --json
leadshoot export --format csv --priority high --out leads.csvEvery search gets its own number. --search latest means the newest search,
not every lead ever discovered. Pipeline stages and notes are preserved
across rechecks.
LeadShoot stores state in ./leadshoot.db. Use --db path/to/project.db or
LEADSHOOT_DB when you want separate projects instead of mixing campaigns.
Paid lead lists sell everyone the same contacts. LeadShoot computes yours - and it's agent-native the way Postiz is for social: you tell your AI what you sell, and it runs discovery, verifies every gap against the real world, qualifies each lead as high/medium/not sure, and works the research queue while you keep talking. Open data, one SQLite file, runs on a laptop.
- ✅ Tell it what you sell. A deterministic niche classifier maps your words to gap mode (find fixable problems) or fit mode (find healthy buyers) - no guessing.
- ✅ Discovery on open data. OpenStreetMap and Overture (~60M places) - no scraping required, no contact list to buy.
- ✅ Evidence before priority. Flagged sites were fetched and seen failing. Anything inferred stays
not_sureuntil confirmed. - ✅ Useful context, not a mystery number. Every lead says
high,medium, ornot_sure, why, what is unknown, and what to check next. - ✅ Bounded quick research.
findreturns independent website/social/review/age jobs that agents can run in parallel. - ✅ Your pipeline stays yours. Stages and notes live in tables no engine run ever touches.
- ✅ A live map, hands-free.
findopens the map in your browser by itself and repaints it as data changes - pins land as each check commits, whether the writer is your terminal or an agent over MCP. No refresh button, no serve step. - ✅ One SQLite file. No accounts, no cloud, portable - point it at your city.
Not a SaaS with AI bolted on - a CLI that speaks JSON, so any agent that can run a shell command can drive it. Say "find cafés in Astoria with dead Instagrams" and your agent runs discovery, live verification, the quick-research queue, and prepares only evidence-backed leads.
Three ways in, widest first:
1. JSON-first CLI - universal. Agent-facing commands either emit JSON
directly (status, niche, options) or accept --json. Parse the JSON;
do not scrape the human-readable tables. Grok, Hermes, OpenClaw, a bash loop,
or cron can all run LeadShoot without MCP.
leadshoot find --icp x --json2. AGENTS.md + the .agents/ hub (8 skills, 5 specialist agents, 5 rules) - agents that read
repo conventions (Claude Code, Codex, Cursor…) pick up how to use it well, automatically.
3. MCP - optional, for MCP-native agents.
claude mcp add leadshoot -- leadshoot mcp # stdio
leadshoot mcp --transport http # HTTP :8322/mcpAgents hold no state; everything lives in one SQLite file. Per-client setup: INTEGRATIONS.md.
| You sell | It finds |
|---|---|
| Websites | businesses with no site, or a dead one |
| Redesigns | outdated WordPress, Wix/GoDaddy templates, slow or non-mobile sites |
| Social media management | accounts gone quiet, tiny audiences |
| Booking / ordering software | busy places you can't book online |
| Reputation management | weak or thin reviews |
| A product - coffee, food, equipment | healthy buyers: established, well-reviewed, active |
| Anything else | your own gap mix (--gaps) |
Not sure how your niche maps? Ask the engine - it answers deterministically:
$ leadshoot niche "I roast specialty coffee beans"
{"mode": "fit", "categories": ["cafe", "restaurant", "bakery", "hotel"],
"say": "You sell a product, not a fix - leads are HEALTHY buyers …"}Gap niches hunt fixable problems. Fit niches qualify healthy buyers: the thriving café that's worthless to a web designer can be a high-priority lead for a coffee roaster.
LeadShoot does not apply one universal score to every local business. The offer determines which facts matter:
| Seller | Mode | A strong candidate | What stays not_sure |
|---|---|---|---|
| Web designer targeting dentists | gaps |
the official site was fetched and a fixable website problem was observed | a directory has no website field, but no independent check confirms the business lacks one |
| Social-media agency targeting salons | gaps |
an official profile is found and its public activity signal confirms a long period without posting | a likely profile exists, but its identity or latest activity has not been verified |
| Bookkeeper targeting independent cafés | fit |
an operating café has positive review, activity, and maturity signals | the café exists, but the public facts needed to judge buyer fit are still missing |
This distinction is the core qualification model: gap mode needs evidence of a problem the seller can fix; fit mode needs evidence of a healthy prospective buyer. Missing data is never silently converted into either one.
![]() |
![]() |
Every lead explains itself: its priority, decisive evidence, what is still
unknown, and the next action. A not_sure lead is a research candidate, not a
claim dressed up as a number.
| Priority | Meaning |
|---|---|
high |
enough verified evidence to justify personalized outreach |
medium |
plausible lead, but only partly qualified |
not_sure |
a decisive fact is missing or the apparent gap is unverified |
The meaning of positive evidence follows the niche. In gap mode, a verified problem raises priority. In fit mode, operating health, reviews, activity, and maturity raise priority; LeadShoot does not manufacture a problem just to justify outreach.
Start with the open osm provider. For a high-coverage Google-first funnel,
explicitly select Google Maps only after accepting its terms tradeoff and
configuring your own
gosom/google-maps-scraper:
leadshoot icp new --name boston-social \
--area "Boston, Massachusetts, United States" \
--categories salon --service social_media --provider gmaps --json
leadshoot find --icp boston-social --json
leadshoot research-queue --search latest --jsonfind runs the user's configured gosom scraper first, checks listed websites
concurrently, returns high / medium / not_sure leads, and includes the
bounded jobs needed to resolve uncertain candidates. Google-backed ICPs also
accept any free-text business type, so the funnel is not limited to the
built-in OpenStreetMap category dictionary. See
INTEGRATIONS.md for configuration and manual import
instructions.
The full command set (enrich, mark, recheck, export, ui, mcp,
serve) is in INTEGRATIONS.md.
ICP → discovery → concurrent checks → qualitative priority → research queue → ready leads
- Every
findis a numbered search - results never silently accumulate. - Verified means verified: flagged sites were fetched and seen failing.
Everything inferred stays
not_sure. - Fit-mode product sellers need no artificial “problem”; good reviews, activity, and operating maturity are positive buyer evidence.
- Your stages and notes live in separate tables no engine run ever touches.
| Data | Notes | |
|---|---|---|
osm (default) |
OpenStreetMap | fully open, polite Overpass queries |
overture |
Overture Maps, ~60M places | open CDLA license, queried on S3 via DuckDB - ~10× OSM's salon coverage in our Manhattan test |
gmaps |
your own gosom run | against Google's ToS - your call, never a default; harvested emails refused |
The map says “Waiting for leads.” Check that find is still running. A
large search may need several minutes. When it finishes, run leadshoot ui
or press Refresh.
The businesses are in the wrong country or region. Save the ICP with a
fully qualified place such as "Cambridge, Massachusetts, United States"
instead of an ambiguous city abbreviation, then run a new search.
An area returns suspiciously few businesses. Try the wider administrative
area or the overture provider. OpenStreetMap coverage varies by location.
Google mode says its scraper is missing. Google discovery is not bundled. Install your own gosom binary and configure its absolute path as described in INTEGRATIONS.md.
You want a completely separate campaign. Point the command at a new database instead of deleting the current one:
leadshoot status --db campaigns/new-campaign.dbBusiness contacts only - no email harvesting, ever. robots.txt honored on every
fetch. No outreach layer by design; if you email, CAN-SPAM has no B2B exemption.
Full rules: .agents/rules/.
SearXNG confirm-pass for no_website · deep link audits (muffet) · screenshot
evidence (shot-scraper) · cross-provider dedupe · Geofabrik country-scale ingest ·
PyPI (pipx install leadshoot)
The most useful feedback is a real niche, a real place, and the result that felt wrong or incomplete. Use the qualification feedback form to report:
- what you sell and which local businesses you target;
- whether LeadShoot chose gap mode or fit mode;
- which evidence, unknown, priority, or next action needs improvement.
Please remove personal data, private notes, and review text before sharing output. Public business facts and aggregate review signals are enough to reproduce qualification problems.
See CONTRIBUTING.md for the complete development and pull
request workflow. In short, edit .agents/ when changing agent-facing
material, run python scripts/sync_agents.py, and keep pytest green:
python -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest tests/ -qMIT. Map data © OpenStreetMap contributors (ODbL); places © Overture Maps Foundation (CDLA-Permissive-2.0). Agent logos are trademarks of their respective owners, shown to indicate compatibility.








