All articles
    Case Studies

    ProLeads: Building an AI Lead-Generation Tool (Product Teardown)

    ProLeads AI lead generation turns a plain-English description of an ideal customer into a structured query, runs it against a Postgres company database plus live web extraction, then enriches matches with contact details. I built it on Supabase, Firecrawl and an LLM extraction layer. The hard part is contact coverage, not search.

    Syed Husnain Haider Bukhari
    8 min read

    Every lead-generation product demo looks the same. Someone types a sentence, a table of companies appears, the audience nods. The demo hides the only question that decides whether the product works: can you actually reach the people in that table?

    ProLeads, the B2B lead-finding product I built is my answer to that question, and this is an honest teardown of it. Architecture, the enrichment pipeline, the mistake that cost me months, and what I would build differently on day one.

    What is ProLeads and what problem does it solve?

    ProLeads is a B2B lead-finding SaaS that lets a user describe their ideal customer in plain English instead of filling in a filter form. Type "HVAC contractors in Dallas with fewer than 50 employees" and it returns matching companies with contact details, source URLs and a confidence signal.

    The reason it exists: filter forms encode the vendor's schema, not the buyer's mental model. A sales rep does not think in NAICS codes and employee-count buckets. They think in sentences. Closing that gap is the whole product thesis behind ProLeads AI lead generation.

    The stack, for anyone evaluating a similar build:

    • Vite plus React front end, deployed as a single-page app.
    • Supabase for Postgres, auth, row-level security and Edge Functions, which run on a Deno-compatible runtime according to Supabase's docs.
    • OpenAI models for query parsing and for structured extraction from scraped pages.
    • Firecrawl for live web retrieval, Hunter.io as an email discovery fallback.
    • A Python worker using Scrapling for high-volume directory scraping, fed by a Postgres job queue.

    How the ProLeads AI lead generation pipeline is wired

    The ProLeads AI lead generation flow splits into two distinct paths that share one output schema: a fast path over the curated database, and a slow path that goes out to the live web. Users never see which one ran.

    A single query moves through these stages:

    1. 1Parse. An LLM converts the natural-language prompt into a typed filter object: industry, geography, employee band, technology signals, exclusions. This is a constrained extraction task with a JSON schema, not free-form generation.
    2. 2Route. If the curated table can plausibly satisfy the filters, the query hits Postgres first. Otherwise it falls through to live retrieval.
    3. 3Retrieve. The database path runs full-text search and structured filters against the company table. The live path uses Firecrawl to pull directory and company pages.
    4. 4Extract. Scraped HTML goes back to the model with a strict output schema: company name, website, phone, email, address, employee estimate. Anything the page does not support comes back null rather than guessed.
    5. 5Enrich. Rows missing an email get a Hunter.io domain-search lookup, which returns addresses discovered for a domain along with sources and a confidence score.
    6. 6Score and dedupe. Records are normalised, matched against existing rows, and assigned a quality tier that governs whether they are shown at all.

    Step four is where most teams lose accuracy. If you let a model free-write the extraction, it will invent a phone number that looks plausible for the region. Forcing a schema and permitting nulls turns hallucination into a missing field, which is a problem you can fix later. That discipline is the same one I apply across every AI automation build.

    "A null field is a task. A hallucinated field is a churned customer."

    Deduplication is the stage everyone underestimates

    The same company arrives as "Acme HVAC LLC", "Acme H.V.A.C." and "acmehvac.com" from three different sources. Exact-match deduplication misses all three. In ProLeads AI lead generation the primary key for matching is the normalised domain, because domains are the closest thing B2B data has to a stable identifier.

    Where no domain exists, the fallback is a normalised name plus locality comparison, and pgvector embeddings are a reasonable tiebreaker for near-duplicate names. Do the cheap deterministic checks first. Embedding every row before you have tried string normalisation is expensive theatre.

    Why natural-language search, and where it stops helping

    Natural-language search wins on the first query and loses on the tenth. It removes the learning curve for a new user, but experienced users want repeatability, and language is a lossy way to express a saved segment.

    So the parser does not return results directly. It returns a filter object that is rendered back to the user as editable chips. They can see exactly what "mid-sized" was interpreted as and change it. The model proposes, the structured query executes, and the user can always overrule the interpretation.

    That pattern generalises well beyond lead generation. I used the same proposal-and-confirmation loop in AgentFlow, the visual agent builder, where an LLM drafts a branch and the operator edits the node rather than re-prompting.

    What is the contact-data coverage problem?

    The contact-data coverage problem is simple to state and brutal in practice: company records are abundant and cheap, while verified phone numbers and email addresses are scarce and expensive. Most public company datasets contain no contact information at all.

    I learned this the expensive way. I filled the ProLeads database with large public company datasets, watched the row counter climb past 100,000, and shipped a database browser on top of it. Then the search results came back empty, because the front end filters for rows that a user can actually contact.

    The real state of the inventory once I audited it by source:

    SourceApprox. rowsContact dataUsable as inventory
    Crunchbase-style org export100,000NoneNo
    Public company registry dump6,400NoneNo
    Early manual imports58Phone and emailYes
    Yellow Pages scraper output30 per jobPhone and websiteYes

    Roughly 99.9% of the rows were unreachable. The product was not broken in code. It was broken in inventory, and no amount of prompt engineering fixes an empty warehouse. That audit is the single most useful hour I spent on the project.

    The second-order bug nobody catches in review

    The same import wrote "North America" into the country column. Every user query filtering on "United States" returned nothing, even for the handful of rows that did have phone numbers. Schema-level validation on ingest would have caught it in seconds. I now treat ingest validation as non-negotiable in any data engineering work.

    How do you fix a lead database with no contacts?

    You stop importing and start enriching. Two levers matter, and they are ordered by cost per contactable row, not by how interesting they are to build.

    The recovery sequence I ran:

    1. 1Audit by source. Group every row by import batch and count how many have a phone, email or website. Delete nothing yet; you need the denominator to make budget decisions.
    2. 2Enrich what you already have. Companies with a known domain are enrichment candidates. Hunter's domain search and enrichment endpoints turn a domain into discovered addresses with sources, which is far cheaper per contact than acquiring a fresh dataset.
    3. 3Scrape sources that publish contact details by design. Business directories exist to be contacted. A Scrapling worker pulling Yellow Pages listings returned 30 HVAC contractors in Dallas with phone and website in about ten seconds, which beats a hundred thousand contactless rows.
    4. 4Gate on quality, not on quantity. A quality tier on every row, with the UI filtering to contactable records, means a small honest inventory outperforms a large dishonest one.
    5. 5Re-verify on a schedule. Contact data decays. Treat freshness as a field with a timestamp, not an assumption.

    The scraping half of this is a solved engineering problem if you have done it at volume before. I have run distributed scraping at 100K+ jobs per day, and the pattern is the same: a Postgres job queue, idempotent workers, per-domain rate limits, and structured output written back through a single writer.

    What does it cost to run an AI lead generation tool?

    Costs split into four lines, and only one of them is the LLM. Vendor prices and billing rules move constantly, so check each provider's official pricing page rather than trusting a number in a blog post, including this one.

    How the cost structure behaves as you scale:

    Cost lineScales withControllable by
    LLM parsing and extractionTokens per page multiplied by pages processedCaching parsed queries, truncating page bodies before extraction
    Web retrieval and scrapingPages fetched per searchServing from the database first, capping result depth
    Contact enrichment creditsLookup volume, under whichever billing rule the provider appliesOnly enriching rows with a known domain
    Database and hostingStored rows and query volumePruning contactless rows, indexing the filter columns

    The counter-intuitive line is enrichment, because billing rules differ per vendor and you have to read them before you design the pipeline. Hunter's docs, for example, state that a query is counted for calls returning at least one result and that no credit is charged when the Email Finder returns nothing; other providers meter differently. Either way, firing lookups at rows with no domain spends request budget proving that unreachable companies are unreachable, so filtering before enriching is the highest-leverage cost control in the system.

    Should you build a lead tool or buy one?

    Buy, unless your differentiation is the data itself. If you need a list of software companies in London, an existing provider already has better coverage than you will build in a year, and their coverage is their moat.

    Building is justified when at least two of these are true:

    • Your target segment is under-served by mainstream providers, such as local trades, regional directories or non-English markets.
    • You already own a proprietary signal, like product usage data or first-party intent.
    • You need lead data embedded inside an existing workflow rather than exported as CSV.
    • Per-seat licensing at your headcount costs more than an engineer maintaining a pipeline.

    For most SaaS teams the honest answer is to buy the data and build the workflow around it. I have written up that trade-off in more detail in the build versus AI SaaS tools comparison.

    What I would do differently on day one

    I would define "contactable" before writing a single line of ingest code, and refuse any row that fails the definition. Everything else in the teardown follows from that one decision.

    The four rules I now start with:

    • Contact coverage is the product metric. Report it on the admin dashboard above every other number.
    • Validate on ingest with a schema, including enumerated country and region values.
    • Ship one narrow vertical with real coverage before broadening the schema.
    • Instrument empty results. A search that returns nothing is a data signal, not a UI edge case.

    None of this is exotic engineering. It is the unglamorous discipline that separates a lead tool that closes deals from one that demos well. If you are weighing up who should build something like this with you, my buyer's guide to working with an AI automation expert covers how I scope this kind of project.

    Key takeaways

    • ProLeads AI lead generation converts a plain-English prompt into a structured filter object, then executes it against Postgres or live web retrieval.
    • Forcing a strict JSON schema with nullable fields during extraction converts hallucinated contact details into honest missing data.
    • Contact coverage, not row count, is the metric that determines whether a lead-generation product works.
    • Enriching companies you already hold with a known domain is cheaper per contactable record than importing another dataset.
    • Directory scraping produces fewer rows than a bulk import but almost all of them are reachable.
    • Build a lead tool only when the data itself is your differentiation; otherwise buy the data and build the workflow.

    Frequently asked questions

    What is ProLeads?
    ProLeads is a B2B lead-generation SaaS I built that accepts a plain-English description of a target customer and returns matching companies with contact details. It runs on Vite and React with Supabase Postgres, Supabase Edge Functions, OpenAI for query parsing and extraction, Firecrawl for live retrieval, and Hunter.io for email discovery.
    How does natural-language lead search actually work under the hood?
    An LLM performs constrained extraction, converting your sentence into a typed filter object with fields such as industry, location and employee band. That object is rendered back as editable chips and then executed as a normal database query. The model interprets intent; deterministic SQL returns the rows.
    Why do most lead databases have no contact information?
    Because company records and contact records come from different sources. Registries, funding databases and web crawls yield organisation names, domains and descriptions cheaply. Verified phone numbers and email addresses require discovery, verification and constant refreshing, so they are sold separately and priced far higher.
    How much does it cost to run an AI lead generation tool?
    Costs come from four lines: LLM tokens for parsing and extraction, web retrieval per page fetched, contact enrichment credits, and database hosting. Enrichment often dominates at scale, and each provider meters it differently, so read the billing rules alongside the price. Check every vendor's official pricing page, since these terms change frequently.
    Is it worth building your own lead-generation tool?
    Only if the data is your differentiation. If you need mainstream B2B lists, an established provider already has better coverage than you will build. Building makes sense for under-served verticals, proprietary intent signals, or when lead data must live inside an existing workflow rather than a CSV export.
    What is the difference between lead scraping and lead enrichment?
    Scraping discovers new records from sources such as business directories, producing companies you did not previously hold. Enrichment adds missing fields to records you already have, typically turning a known domain into email addresses or phone numbers. Scraping grows the list; enrichment makes an existing list contactable.
    Can an LLM be trusted to extract contact details from a web page?
    Yes, with a strict output schema that permits null values. Given a JSON schema and an instruction to return null for anything the page does not support, extraction is reliable. Given free-form output, models will produce plausible-looking phone numbers that do not exist. Always validate formats after extraction.
    How do you measure whether a lead database is any good?
    Measure contact coverage: the percentage of rows carrying a verified phone or email, segmented by import source and by age. Then measure the share of user searches returning zero results. Those two numbers predict churn far better than total row count, which is a vanity metric.

    Sources

    Tags:
    ProLeadsLead GenerationSupabaseData EngineeringAI Products
    HB

    Written by Syed Husnain Haider Bukhari

    AI engineer, data scientist, and founder of Revolutionary Technologies LLC. Ships production AI agents, automations, and data platforms for teams in the US, UK, and UAE — including AgentFlow, AI Walay, and ProLeads.

    Get in touch →

    Related pages

    Want this built instead of researched?

    Book a 30-minute scoping call. You get a one-page plan and a fixed-scope quote within 48 hours.

    Start a conversation

    Let's Create a Revolution