Building an Automated Lead Generator with Strict TDD

Finding clients for a software consulting side hustle is usually harder than the actual work. You end up scrolling Google Maps, clicking broken links, and trying to guess whether a business has the budget for digital work.

So I built the Local Business Lead Generator to automate the hunt for high-value service businesses (landscapers, plumbers, HVAC) that are successful enough to pay for services, but outdated enough to need them. Here’s how it works: strict TDD, tiered analysis, and deep platform fingerprinting.


The Core Architecture

The system is a modular pipeline that takes a location and industry and outputs a prioritized list of leads. Concerns are cleanly separated into API (data ingestion), Analysis (business logic), and Models (data structures).

1. Data Ingestion (The Search Engine)

I use SerpAPI to scrape Google Maps results. Unlike generic web search, Maps data gives you the context needed for scoring, specifically, the Maps ranking.

2. The Tiered Analysis Pattern

This is the most technically interesting part. I needed to analyze websites for SSL, mobile responsiveness, and tech stack, and the obvious approaches both had problems. requests is fast but fails on JavaScript-heavy sites (Wix, Squarespace). Playwright is accurate but slow and resource-heavy. So I went with a hybrid tiered approach instead.

You get the speed of requests for 80% of sites and the accuracy of Playwright for the messy 20%.

3. Deep Platform Fingerprinting

To sell anything effectively, you need to know what the client is already using. The analyzer doesn’t just check if a site exists, it fingerprints the stack from HTML signatures.

I look for specific artifacts in the DOM and headers to identify:

If a client is running Salesforce but their website is broken, they’re a high-value target, they invest in tech but implemented it poorly. If they’re on ClickFunnels, they already understand conversion funnels and might be ready for automation. The signals change what you’re selling, not just whether to pitch.


The QA-First Approach: Strict TDD

Given my background in QA, I wasn’t going to build this as spaghetti code. Every component went through a strict red-green-refactor cycle using Python’s standard unittest library.

Current status: 87 tests passing, 100% pass rate.


The Scoring Algorithm

Data is useless without prioritization. The LeadScorer module calculates an Opportunity Score (0-100) based on weighted factors:

  1. Maps Ranking (40%): higher rank = more established business = higher budget
  2. Website Quality Gap (30%): lower quality = bigger pain = easier sale
  3. Social Presence (20%): missing social channels = low-hanging fruit for upsells
  4. Platform Opportunity (10%): specific stacks (outdated WordPress, etc.) suggest clear upgrade paths

The output isn’t just a number either, it generates human-readable reasoning:

Top ranking business (Rank #2) but has Poor website quality (Score: 25). High-value target with strong local presence.


What’s Next: The Intelligence Layer

The current system runs on deterministic rules. The next phase is an LLM layer that replaces hard-coded logic with semantic understanding:

What started as a lead-finding tool turned into a decent case study for how TDD and solid error handling can turn a simple scraper into something you actually trust to run unattended.