Use case
Build a regional construction lead list in one run
How to turn a sales territory into a deduplicated list of licensed building trades — and why doing it one trade at a time quietly costs you duplicates.
A building-products distributor takes on the North Bay. The territory is four counties — Sonoma, Napa, Solano, Contra Costa — and the buyers are the trades that specify and install: general builders, framers, concrete, drywall, electrical, HVAC, plumbing, roofing.
The list has to come from somewhere defensible. Licensed contractors are a matter of public record in California, which makes CSLB the obvious source: it is the state’s own register, it carries phone and address, and it says whether the licence is current.
The way this usually goes
You open CSLB’s export form and discover it takes one classification at a time. Thirteen trades across four counties is fifty-two downloads.
So you script it, or you click it, and you end up with fifty-two spreadsheets. Then the real problem starts: contractors hold more than one classification. A shop with a B general building licence and a C-39 roofing licence is in two of your files. One with B, C-10 and C-39 is in three.
Merging that by hand is where lists go wrong. Miss the dedup and your “1,400 prospects” is really 1,100 — and about 300 of them are going to get the same email twice, from a distributor introducing themselves.
The way that works
{
"licenseClasses": ["B", "C-5", "C-8", "C-9", "C-10", "C-20", "C-36", "C-39"],
"counties": ["Sonoma", "Napa", "Solano", "Contra Costa"]
}
One run. Every county is fetched in the same request per trade, so the four-county territory costs no more requests than one county would. Results are deduplicated by licence number before you are charged, so the shop with three classifications is one row and one charge.
What you can do with the output that you couldn’t with spreadsheets
Segment without losing the whole picture. Each row carries matched_trade — which of your
requested trades found it — alongside classifications, everything the contractor actually holds.
So you can run a roofing campaign off matched_trade == "C-39" while still knowing that a third of
that list also holds a general building licence, which changes what you’d say to them.
Qualify on licence health. status, expiration_date and bond_number are in every row. A
licence expiring in ninety days is a different conversation from one that just renewed.
Spot the self-insured. workers_comp_type distinguishes a real policy from an exemption. An
exemption usually means an owner-operator with no employees — a different-sized customer than the
name alone suggests, and worth knowing before you route it to a field rep.
Re-run it and diff. Point it at the same territory next month and compare licence numbers. New entries are newly licensed contractors in your patch, which is about as warm as a cold lead gets.
What it costs
The eight trades above across those four counties come back around 1,700 deduplicated contractors — roughly $3.40 at $0.002 each. The four-county sweep for a single trade returned 1,096 rows, so you can size any territory by running one trade first and multiplying.
Larger counties scale steeply. If you want a bounded bill while you’re exploring, maxResults caps
per trade.
What it won’t give you
No email addresses — CSLB doesn’t publish them, and any actor claiming otherwise on this source is enriching from somewhere else. Contact details are what the contractor filed with the state, which can lag their current website. And it is California only; the register is a state one.
Start with the how-to guide for the full input reference, or run California Construction Trades Directory & Leads directly. To verify a specific contractor rather than build a list, use California Contractor License Lookup & Verify.
Last updated 2026-08-19