Skip to content

Repository files navigation

English · 简体中文

LeadRadar · Lead Sourcing & AI Scoring

Turn a messy list of names into a ranked action list with ready-to-send outreach emails — so your sales team never has to guess who to call first.

Dashboard


What problem it solves

The most time-consuming part of sales isn't the pitch — it's figuring out which few names in a giant list are actually worth a phone call. LeadRadar hands that job to AI:

  • Define your ideal customer in plain English — describe your ICP in a sentence (e.g. "cross-border e-commerce teams, 30-200 people, with a DTC storefront, currently hiring for ops") instead of wrestling with a rigid filter form. Change the profile any time and every score updates with it.
  • AI scoring isn't a black box — every lead gets a 0-100 score, an A/B/C tier, a one-line reason, and a list of matched and missed signals, so your reps understand why, not just a number.
  • One-click CSV import — bulk-import an existing list, or add a single lead by hand.
  • Outreach emails, not mail-merge templates — a personalized first-touch email is generated per lead in both English and Chinese, referencing that lead's actual industry, size, and notes — not variable substitution.
  • Failures are visible, not swept under the rug — a scoring failure (missing fields, model refusal, network issue) is saved to the record and shown inline, right in the row, no hovering required.
  • Works with zero API keys — without an ANTHROPIC_API_KEY, LeadRadar automatically runs in demo mode with deterministic rule-based scoring and template emails, so you can try the full workflow before wiring up a real key.

Tell the AI who you're looking for, in one sentence

No dropdowns, no filter builder — just plain language: "Cross-border e-commerce teams with 30-200 employees that already run a DTC storefront and are actively hiring for operations roles, in industries like cross-border e-commerce, DTC brands going global, and owned-audience retention marketing." That sentence is the single source of truth behind every score and ranking — edit it once, and every lead re-sorts.

ICP input

Every lead gets a score — and a reason

Click into any lead to see its 0-100 score, A/B/C tier, matched and missed signal badges, and a personalized English and Chinese outreach email — not a template with variables swapped in, but copy that actually references the company's industry, size, and storefront status. Your reps don't get a lonely number; they get a complete case for whether this call is worth making.

Lead detail: score, signals, and outreach email


Quick start

git clone https://github.com/LuciferLiu/leadradar.git
cd leadradar

python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt

cp .env.example .env          # add ANTHROPIC_API_KEY if you have one — works fine without it (demo mode)
python seed_demo.py           # generates demo data: 1 ICP + 12 leads
uvicorn app.main:app --reload --port 8030

Open http://localhost:8030

The demo data is scored by the built-in deterministic engine, so you can run the entire import → score → generate email → export flow offline.

Docker

cp .env.example .env
docker compose up -d

Configuration

Everything is controlled through environment variables — see .env.example:

Variable Default Description
DATABASE_URL sqlite:///./leadradar.db Swap in a Postgres connection string to change databases
ANTHROPIC_API_KEY empty Leave unset to run fully offline in demo mode
CLAUDE_MODEL claude-opus-5 Model used for scoring and email generation, overridable

Architecture

app/
├── main.py           FastAPI routes + application lifecycle
├── config.py          Environment variables and demo-mode detection
├── db.py              SQLAlchemy engine and session, SQLite foreign-key PRAGMA
├── models.py           Data models: Lead, ICPProfile
├── schemas.py           Pydantic request/response models + Claude structured-output contract
├── service.py           Core logic: ICP storage → AI/rule-based scoring → email generation
└── static/
    └── index.html      Dashboard (vanilla JS, no build step)

Three deliberate trade-offs went into this design:

Scoring is a pure function, and AI and the rule engine share the same output contract. Whether it goes through Claude's structured output (messages.parse) or the demo-mode keyword matcher, ScoreResult (score / tier / reason / matched_points / missed_points) comes back in exactly the same shape. main.py and the frontend never need to know whether a model call actually happened — the switch lives in exactly one place, in service.py.

The ICP is a paragraph of natural language, not a filter form. A sales profile is inherently fuzzy — a signal like "currently hiring for ops" doesn't fit cleanly into a dropdown. Handing that interpretation to an LLM matches how salespeople actually think about their targets, far better than forcing it into structured fields. The cost is that you lose precise SQL filtering — but scoring itself is already a fuzzy match, so that trade-off is one worth making.

Failures are persisted, not retried or swallowed. Scoring and email generation fail independently of each other; the failure reason is written straight back onto that row instead of going into a log file or a toast that disappears in three seconds. The thing a sales dashboard can least afford is a lead that just sits there with no score, with no way to tell whether it was never tried or it failed. So failure is treated as a definite state, not a missing one.


API

Once the service is running, interactive docs are available at /docs.

Method Path Description
GET /api/leads List leads, sorted by score descending (unscored leads last)
POST /api/leads Add a single lead by hand
POST /api/leads/import Bulk import via CSV (multipart form, field name file)
PATCH /api/leads/{id} Update lead fields
DELETE /api/leads/{id} Delete a lead
POST /api/leads/{id}/score Score a single lead
POST /api/leads/score-all Score every lead in bulk
POST /api/leads/{id}/email Generate an English + Chinese outreach email for a lead
GET /api/icp Get the current ICP profile
PUT /api/icp Update the ICP profile
GET /api/export Export all leads as CSV (including score, tier, reason)
GET /api/stats Dashboard summary metrics

CSV import example:

curl -X POST http://localhost:8030/api/leads/import \
  -F "file=@leads.csv"

CSV headers can be in either English or Chinese, pick whichever: company_name/公司名, website/网站, industry/行业, company_size/规模, contact_name/联系人, contact_email/邮箱, notes/备注. Company name is the only required field — rows missing it are skipped, with reasons listed in the response.


Notes

  • In live mode, both scoring and email generation go through Claude's structured output (messages.parse), costing a few hundred tokens per lead — try a small batch before scoring a large list in bulk.
  • The demo-mode scoring engine is keyword matching plus a size-range comparison, meant only to exercise the full workflow and preview the UI — it doesn't represent real semantic understanding. Score quality improves substantially once a real key is wired up.
  • The ICP profile and leads are currently single-tenant (one ICP for the whole database) — multi-team setups will need to add their own tenant isolation.

License

MIT

About

AI lead scoring & outreach — rank prospects against your ICP and draft personalised first emails

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages