← All projects

Supporting / Engineering case study

Leadworks

An automated lead generation and enrichment engine for UK trade businesses.

Python / SQLAlchemy / SQLite / Google Places API / Companies House API / Excel

Observed evidence

Source + observation + confidence

Website audit, Google and Companies House observations remain separate from the ranking.

Calculated output

Score + reasons + missing data

Change the weights, reuse the observations. This is an explanation of the data model; no real lead records are shown.

01 / The problem

The problem

A salesperson needs a usable, explainable call list. Directory data alone is incomplete, enrichment can match the wrong company and a ranking can look precise while measuring very little.

02 / What I built

What I built

A local workflow discovers businesses by trade category, deduplicates them, audits their websites, enriches from Companies House and produces ranked Excel lists. Sales feedback can be imported back. A CLI and spreadsheet are deliberate choices for one salesperson.

03 / Architecture

Architecture

  1. Trade category → Google Places discovery
  2. Deduplicate → raw observations
  3. Website audit + Companies House enrichment
  4. Flattened features → pure scoring core
  5. Score + confidence + factors + missing data
  6. Ranked Excel call list → salesperson feedback import

04 / Key decisions

Key decisions

01

Keep observations separate from scores

Raw evidence is stored independently of calculated output. Changing weights can rescore the same observations. Pure functions return score, confidence, band, positive and negative factors, and missing data.

02

Make uncertainty visible

Enrichment matches carry confidence and evidence. Missing data is not automatically negative data. Rankings describe observed digital need, reachability and business signals; they do not predict conversion.

03

Refuse spend before making a paid call

Minimal Google field masks, no unnecessary Place Details calls, free postcode geocoding and dry-run estimates keep cost explicit. A hard request/spend guard runs before paid calls; a test proves an over-budget run makes zero HTTP requests.

04

Choose an official discovery API

Checkatrade automation was deliberately not built after a terms review prohibited it, despite permissive robots.txt. Google Places provided the official programmatic route instead.

05 / What went wrong

What went wrong

A confident score can measure nothing

Problem
Some scoring signals looked informative while providing no independent evidence.
Why it happened
Directory completeness penalized fields Google Places could never supply. Phone consistency compared values populated from the same source field.
How it was detected
A review after changing the discovery source examined what each signal actually observed, even though tests passed.
Response
The v0.2 revision excluded unobservable inputs and compared directory numbers with independently observed website numbers. Other scoring and feedback defects remain open.
Lesson
Check the provenance of both sides of a comparison. A passing test cannot prove that a signal means what its name suggests.

06 / Evidence

Evidence

  • Approximately 858 businesses across five trade categories in around an hour of discovery, for a few dollars of Google API spend.
  • Three enrichment sources: Google, business websites and Companies House.
  • The over-budget test verifies zero HTTP calls. No actual lead contact data is displayed here.

07 / Current status & limitations

Current status & limitations

Local tool · CLI / SQLite / Excel

Local CLI workflow. Sales-outcome data is insufficient to validate conversion prediction. Known scoring and sales-feedback defects remain; the architecture and discovery result should not be read as validation of every ranking.

Evidence basis: project implementation review and research notes supplied for this portfolio, September 2026. Project tests described here are distinct from the portfolio’s own checks.

Next case studyTheAnythingSorter