LiveLead: Live Google Search Data → Explainable Sales Leads in One Command
An open-source CLI built for the SerpApi India Hackathon 2026 — give it a business category and a city, and it returns real businesses with phone numbers, ratings, and an explainable 0–100 lead score. Standard library only: no dependencies to install.
TL;DR: LiveLead takes a query like "dental clinic" and a location like "Birmingham, UK", pulls live business listings through SerpApi's Google Maps and organic Search engines, enriches each listing with hours and web-presence checks, and scores it 0–100 with human-readable reasons ("has phone", "no website found → needs a website pitch"). Output lands in JSON or CSV, sorted by score, ready for outreach. The entire tool is one Python file with zero third-party dependencies — the only key you need is a free SerpApi account (100 searches/month).
The problem: lead lists are dead on arrival
Anyone who has done B2B outreach knows the feeling. You buy or scrape a "lead list" of 500 businesses, and by the time you're dialing, a third of the phone numbers are wrong, half the websites are dead, and nobody told you which entries were worth calling first. Static lists rot; the businesses you're pitching move, close, rebrand, and add phone numbers every day.
LiveLead takes the opposite approach: don't maintain a list — query the live web at research time. Every data point in its output comes from a live SerpApi call made during the run. No cached data, no static lists. The output is only as fresh as the search index, which is exactly the point: if you're researching leads on a Tuesday morning, you want Tuesday morning's data.
It was built as a SerpApi India Hackathon 2026 entry, and like good hackathon software, it's deliberately narrow: it solves one workflow — find real businesses in a category + city, figure out which ones are worth pitching, and flag the ones that need a website — and does it in a single command.
How it works: three live SerpApi engines, one scoring pass
The pipeline in livelead.py runs four stages per query:
| Step | SerpApi engine | What it contributes |
|---|---|---|
| 1. Discovery | google_maps (type=search) | Real businesses matching "query in location": name, phone, website, rating, review count, address |
| 2. Enrichment | google_maps (type=place) | Opening hours, reviews link, business description for each listing |
| 3. Web-presence check | google (organic) | Does the business have a findable website? Drives the "needs a website pitch" signal |
| 4. Scoring | Local heuristic | 0–100 score with explainable reasons, sorted descending |
Discovery: maps search with a coordinate fallback
The maps_search() function calls SerpApi's google_maps engine with type=search and a query like "dental clinic in Birmingham, UK". SerpApi's Maps engine wants a latitude/longitude (ll) parameter alongside the location text, so the code ships sensible defaults — Mumbai, Delhi, and Birmingham coordinates selected by a simple string match on the location:
coords = "@19.0760,72.8777,14z" # Mumbai (default)
if "delhi" in location.lower():
coords = "@28.6139,77.2090,14z"
elif "birmingham" in location.lower():
coords = "@52.4862,-1.8904,14z"
It's an honest, documented shortcut: other cities still work because the free-text query carries the location, but the map center biases toward the default. If you fork this, a --ll flag is the obvious first addition (more on that in the roadmap).
Enrichment and the organic check
For each discovered business, enrich_place() fires a second SerpApi call — google_maps with type=place and the listing's data_id — pulling opening hours, the reviews link, and the business description. Then organic_check() runs a quick organic Google search for "{name}" official website and keeps the top three links. That organic check is what separates "has a website field in Maps" from "is actually findable on Google" — and it's the engine behind the tool's most useful signal.
The scoring model: explainable by construction
score_lead() adds up to 100 points from six live signals:
| Signal | Points | Why it matters |
|---|---|---|
| Has phone | +25 | You can actually reach them — the highest-weight single signal |
| Has website | +20 | Established presence; reachable through another channel |
| Rating ≥ 4.0 | +15 | Reputable businesses respond to professional outreach |
| Reviews 10–200 (sweet spot) | +15 | Active but not so big they're unreachable |
| Has phone but no website found | +15 | The money signal: a reachable business that needs a website pitch |
| Hours listed | +10 | An actively managed listing |
Every point comes with a reason string — "has phone", "rating 4.6", "87 reviews (sweet spot)", "no website found → needs a website pitch" — so a human can audit the score in seconds. The score is capped at 100. An empty business record scores 0, which the test suite pins down explicitly.
Code worth noting
The SerpApi client is ten lines of careful plumbing. serpapi_get() injects the API key into the query string, sets a LiveLead/1.0 user agent, and — the detail that matters in production — retries network failures with a linear backoff while letting API-level errors (bad key, quota exhausted) raise immediately:
def serpapi_get(params, api_key, retries=2):
"""GET SerpApi with key injected; raises SerpApiError on failure."""
q = dict(params)
q["api_key"] = api_key
url = SERPAPI_BASE + "?" + urllib.parse.urlencode(q)
last_err = None
for attempt in range(retries + 1):
try:
req = urllib.request.Request(url, headers={"User-Agent": "LiveLead/1.0"})
with urllib.request.urlopen(req, timeout=30) as resp:
data = json.loads(resp.read().decode("utf-8"))
if "error" in data:
raise SerpApiError(data["error"])
return data
except SerpApiError:
raise
except Exception as e: # network flakiness, retry
last_err = e
time.sleep(1 + attempt)
raise SerpApiError(f"request failed after {retries + 1} tries: {last_err}")
Two honest design notes visible in the code. First, per-place enrichment failures are swallowed (except SerpApiError: return {}) — a flaky place-details call degrades the score instead of killing the run. That's the right call for a research tool, but it means a low score can mean "bad lead" or "enrichment failed"; the reasons list won't tell you which. Second, the main loop sleeps 0.4 seconds between businesses — polite, but quota, not rate limits, is the real constraint: SerpApi's free plan gives you 100 searches a month, and each business costs up to three searches. Budget roughly 30 businesses per free-plan run.
Why stdlib-only is a deliberate choice
It's worth pausing on what isn't in this repo: no requirements.txt, no virtualenv, no Docker. The imports at the top of livelead.py — argparse, csv, json, os, sys, time, urllib.parse, urllib.request — are all batteries-included Python. That means the tool runs on any machine with Python 3, including locked-down corporate laptops and minimal CI runners, with nothing to install and nothing to break when a dependency releases a bad version. For a hackathon entry that judges and users need to run in minutes, that's not laziness — it's the correct engineering tradeoff.
Run it yourself
- Get a key. Free plan at serpapi.com — 100 searches/month, no card.
- Run:
export SERPAPI_KEY=your_key python3 livelead.py "dental clinic" "Birmingham, UK" --limit 10 --out leads.json python3 livelead.py "salon" "Mumbai" --limit 10 --format csv --out leads.csv - Flags:
--limit N(default 10),--format json|csv,--out FILE,--no-enrich(skips place-details calls to save quota). - Tests:
python3 test_livelead.py— 10 checks, all SerpApi calls mocked, no key needed. Scoring edge cases, JSON/CSV writer round-trips, key injection, and the API error path are all covered.
Example output row:
{
"name": "Smile Dental Studio",
"phone": "+44 ...",
"website": null,
"rating": "4.6",
"reviews": 87,
"score": 85,
"reasons": ["has phone", "rating 4.6", "87 reviews (sweet spot)",
"no website found -> needs a website pitch"]
}
The CSV output has the same fields flattened (reasons joined with ;) — importable into any outreach tool or spreadsheet.
Honest status and limits
Status: working demo / hackathon entry. The core pipeline — search, enrich, score, export — is implemented, tested with mocked APIs, and ships with a 3-minute demo script (DEMO_SCRIPT.md) covering the live terminal run, the scoring explanation, and the CSV handoff. It is a real tool, not a stub.
The limits are the kind you'd expect from focused hackathon software:
- Quota-bound by design. Three SerpApi calls per business on a 100-search free plan. This is a targeted research tool (dozens of leads), not a bulk scraper.
- Hardcoded map centers. Only Mumbai, Delhi, and Birmingham get exact coordinates; other cities rely on the text query. A
--lloverride would fix this properly. - Silent enrichment failures. A failed place-details call returns
{}and lowers the score without a flag. For a v1 this is fine; for production you'd want a"enrichment_failed"reason. - No dedupe or persistence. Each run is independent — run it twice and you'll re-spend quota on the same businesses.
Roadmap
- Coordinate override (
--ll) and automatic geocoding for any city. - Result caching — SQLite store of previously researched businesses so repeat runs don't re-spend quota.
- Cross-run dedupe on phone/website before scoring.
- CRM export — direct push to common outreach tools beyond CSV.
- Configurable scoring weights via a YAML profile per vertical (dental clinics and law firms don't value the same signals).
- Async enrichment to cut runtime on larger batches.
Frequently asked questions
Give it a business category and a city; it uses SerpApi's live Google Maps and Search data to find real businesses, enriches each with ratings, phone, website, and hours, and scores them 0–100 as outreach leads with explainable reasons. Output is JSON or CSV.
No — beyond the SerpApi key, which has a free tier (100 searches/month). The tool itself is stdlib-only Python: no pip installs at all.
Each business costs up to three SerpApi searches (discovery, enrichment, organic check), so plan on roughly 30 fully-enriched businesses per free month.
--no-enrich stretches that further.The tool is built for agencies selling websites and digital services: a business with a phone number but no findable website is a reachable prospect with an obvious pitch. The score makes that signal explicit instead of hiding it.
The bottom line
LiveLead is a small, honest tool: one file, zero dependencies, live data, explainable scores. It doesn't pretend to be a lead platform — it's the research step before outreach, done with fresh data instead of a rotting list. The code is public; the free SerpApi tier is enough to try it today.
Try it: github.com/Shivay00001/livelead-serpapi — built by Shivam Kumar, VisionQuantech. Want lead research like this wired into your outreach pipeline? [email protected]