Home / Case study
Public records in, a ranked list out.
A research pipeline built for a small professional services firm that wanted to grow deliberately rather than buy a list. Bought lists are stale, unqualified, and identical to the one your competitor bought. This produces a list nobody else has, where every claim on it can be traced back to the record it came from.
Client details are generalized at their request. Every figure on this page was measured from a real run of the pipeline. Nothing here is projected or estimated.
Knowing your client is not the same as finding two hundred more.
A small firm usually knows the kind of client it serves well. What it does not have is a way to find two hundred more of them, ranked by how likely they are to need the service, without paying for guesswork.
State and county governments publish an enormous amount about the businesses operating in them: corporate filings, trade licenses, permits, liens, insurance coverage, local business registrations. It is free, it is public, and it is scattered across a dozen agencies in formats nobody designed to be joined together.
Five stages, end to end.
Acquire
Bulk files pulled straight from the issuing authorities, roughly six gigabytes across a corporate register, five licensing boards and a municipal registration file. Each carries its publisher, its own generation date and a checksum, so any claim built on it can be dated later.
Build the pool
The files are normalized and joined into one record per business. Around 15,900 businesses across six industries and twelve towns, each linked to its corporate entity, officers, licenses and filing history.
Score
Five weighted components chosen by the client: operational complexity, industry fit, owner accessibility, business health, and how reachable the business actually is. Complexity comes from nine signals detected in the data rather than guessed from a company name. The whole pool scores in under two minutes and reproduces exactly, run to run.
Research the shortlist
The top-ranked businesses go through live reads of records that are not published in bulk: secured-transaction liens, insurance coverage, county records, license detail. Each read is captured, checked that it is genuinely about that business, then parsed into a field carrying its own source and date.
Judge, then deliver
A per-business review asks, component by component, whether the live findings argue the score up or down, with the reason recorded beside every move. The output is a working spreadsheet: every prospect, every field, a ranked shortlist, and a list of direct competitors so nobody wastes a week researching a rival.
Built so a failure can never look like a clean result.
Every value states where it came from
And how confident to be: Verified, Likely, or Reasoned. A figure read from an official file on a dated capture is not treated the same as one inferred from a company website, and the sheet says which.
An absence is written down, not left blank
When a portal refuses or a record does not exist, the engine records what was searched, on what surface, on what date, and what the manual route would be. A blank cell meaning "we could not look" and one meaning "we looked and there is nothing" are different facts, and only one of them is a research finding.
A failure can never look like a clean result
This is the rule the whole design turns on. A connection error that returns nothing looks exactly like a search that found nothing. A check that cannot run looks exactly like a check that passed. Every stage is built so those cases fail loudly instead of quietly producing a plausible answer.
It refuses to hand over a contact it should not
Official filings often carry an address belonging to a registered agent, or to the firm the business already uses for the very service being sold. Those are detected, withheld from any field that could be mailed, and flagged with the reason.
Measured from a real run.
Dependency-light, on purpose.
Python, deliberately dependency-light for auditability, openpyxl for the deliverable, YAML per client so a new client is a configuration file rather than a code change. Selftests run under pytest and again in CI on every push. Live record reads are driven through a browser, resumable, and skip anything already captured.
A second firm is a new configuration file: its towns, its industries, its weights, its bands.
The scoring formula belongs to the client, not to the engineGot a pile of public data and no way to use it?
Describe what you are trying to find in two sentences. I will reply with whether it can be built, how, and what it costs. Usually within a day.