FindLeads — Lead Generation with a Built-in CRM
FindLeads turns Google Places results into a focused outreach pipeline, using “No website found on Google” as a lead signal rather than an unsupported claim.
Deployed · pilot
A lead work queue and acquisition monitor. Broader public access awaits verification of endpoint protection and usage limits.
Status documented



Overview
FindLeads is a repeated-use prospecting console built on the official Google Places API. A focused category-and-location run can collect up to 60 results, while the lead workspace turns durable businesses into a searchable, filterable queue with pipeline metrics, phone links, review counts, contacted state, notes, CSV export, and 25-row pagination. Next.js 16 and React 19 sit over Neon Postgres via Drizzle, with Zod validation at every boundary and a deliberately queue-less async worker built from database job rows, `after()`, and SWR polling.
The Problem
Local prospecting starts with noisy directory results and quickly collapses into a spreadsheet that cannot show what was contacted, what needs attention, or which search produced a lead. The engineering constraint was to create a durable solo-operator workflow without adding authentication or external queue infrastructure.
Questions Addressed
- 01
Can a resumable background job survive crashes and duplicate workers using nothing but Postgres and atomic updates?
- 02
How should durable CRM state coexist with re-runnable search snapshots so a re-scrape never wipes your notes?
- 03
How can scrape capacity, progress, empty states, and failures stay legible during repeated daily use?
Methodology
Places Search & Tier-1 Flagging
Search jobs call Google Places Text Search (New) by category + free-text location. Every response is validated with Zod before anything touches the database, and businesses with no website field become tier-1 leads. Results are stored as per-job snapshots so each search run is reproducible.
A Queue-less, Resumable Job Worker
Instead of Redis or a queue service, jobs are database rows processed via Next.js after() with client polling over SWR. The worker checkpoints progress and reclaims work through single-UPDATE atomic claims, so a crashed or duplicated worker never double-processes — behavior pinned down by integration tests against a real Neon test database.
Prospecting Console & Durable CRM State
Durable CRM state (notes and contacted status) lives in a businesses table keyed by place_id, deliberately separated from per-job lead snapshots. The current console adds pipeline metrics, text search, four filters, phone links, review counts, 25-row pagination, explicit 3-page/60-result run capacity, and pending/success/failure feedback for edits.
Key Results
Key Findings
Postgres is a perfectly good job queue at single-user scale: an atomic single-UPDATE claim gives you crash-safety and duplicate-worker safety with zero new infrastructure.
Splitting durable identity (keyed by place_id) from run snapshots is what makes a scraper re-runnable — state you care about should never live in state you regenerate.
Using Postgres `now()` consistently prevents CRM timestamps from moving backward when application and database clocks disagree.
Conclusion
FindLeads is live as a deliberately narrow solo prospecting tool: acquire a bounded batch, work the lead queue, preserve CRM state across new searches, and export when outreach moves elsewhere. The latest review exercised the real Neon-backed dataset on desktop and mobile, while keeping server-side pagination as a future threshold rather than premature infrastructure.