← All projects

Case study

AuditLeads

From “dentists in Austin” to a ranked list of prospects with a concrete reason to reach out.

Role
Solo — architecture, build, billing, deploy
Context
Live in production
AuditLeads illustration

01 Problem

Agencies and freelancers who sell websites and marketing waste hours manually hunting for local businesses that actually need help — then guessing which ones are worth pitching.

There's no fast way to go from “dentists in Austin” to a ranked list of prospects with a concrete reason to reach out.

02 Approach

  • AuditLeads scrapes local businesses from Google Maps, visits each one's website, and computes an Opportunity Score from real signals (site speed, missing pages, technical weaknesses).
  • The user gets a ranked lead list where a high score means a real, pitchable problem — not a random name.

03 Architecture

  1. 01 · SERVICES

    Two decoupled services: a Next.js app (Prisma/Postgres, NextAuth, Paddle billing) and a standalone Python scraper worker, communicating through a job-queue pattern rather than blocking requests.

  2. 02 · SCRAPE

    The scraper gives each business its own fair-shared timeout so one slow website can't kill an entire batch.

  3. 03 · METER

    Credits are charged only per lead that gets a complete score — never for partial or failed results — enforced with atomic database updates so concurrent searches can't be double-charged or slip through free.

04 Result

Livein production at auditleads.co
E2Ereal signup → search → scrape → score → checkout, verified against production infrastructure
Fullsubscription lifecycle — upgrade, renewal, credit reset, invoicing — working end to end

05 Stack

Application

Next.jsPrismaPostgres (Neon)NextAuth

Services

Python scraper workerPaddle

Ops

SentryVercelRailway