California Contractor License Search (CSLB)
Pricing
from $1.80 / 1,000 company scrapeds
California Contractor License Search (CSLB)
California contractor licenses from the Contractors State License Board: 245 927 rows with business phone (99.8%), county (96.6%), trade classification (100%), plus status, dates, bonds and workers-comp coverage. Filter by county, trade class, status or last update. No proxy, no key, no login.
Pricing
from $1.80 / 1,000 company scrapeds
Rating
0.0
(0)
Developer
Vladimir Chernykh
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 minutes ago
Last modified
Categories
Share
California Contractor License Scraper — CSLB
Build a contractor lead list from California's Contractors State License Board export: 245 927 licences, published as one CSV file by the state itself. No login, no cookies, no captcha, no proxy.
Every record carries a business phone number (99.8 %), the county (96.6 %) and the trade classification (100 %) — the three things you need to sort, territory and pitch a contractor list. That is why this is the lead-oriented California actor; for corporate-registry data (officers, agents, EIN, filing history) use the Secretary of State actor instead.
What you get
One row per licence, in a stable shape — every field is always present, and
what CSLB does not publish is null rather than missing.
| Field | Source | Notes |
|---|---|---|
name | BusinessName | the name printed on the wall, upper case |
legalName | FullBusinessName | the full legal form of the name |
businessType | BusinessType | Corporation 70 763, Sole Owner 56 934, Limited Liability 12 720, Partnership 1 614, JointVenture 388 |
entityType | derived from BusinessType | CORPORATION, SOLE PROPRIETOR, LLC, … — mapped, see below |
documentNumber | LicenseNo | California licence number |
phone (businessPhone, businessPhoneDigits, businessPhoneE164) | BusinessPhone | (925) 383 0487, 9253830487, +19253830487 |
county | County | 50 real counties |
mailingAddress | six address columns | street1, city, state, zip, country; country is blank in the source and filled as United States |
classifications | Classifications(s) | trade classes, pipe-separated in the source |
licenseStatus | PrimaryStatus | CLEAR, Contr Bond Susp, Work Comp Susp, … |
isActive | PrimaryStatus | true only for CLEAR |
issueDate, expirationDate, inactivationDate, reactivationDate, lastUpdate | five date columns | ISO 8601 |
workersComp, bonds | WC + three bond blocks | insurer, policy, effective/expiry/cancellation dates |
discipline, pending, asbestosRegistration | four status columns | false on a clean licence |
sourceUrl | built from LicenseNo | see the caveat below |
Always null / empty because CSLB does not publish them:
ein, dbaName, statusDate, jurisdiction (fixed to California),
formationDate, dissolutionDate, principalAddress, registeredAgent,
officers, annualReports, lastEvent.
A licence is not an incorporation. issueDate is the licence date, not the
company's formation date, so it is delivered as its own field and
formationDate stays null rather than being filled with a half-truth.
How to use it
Pick a county, pick trades, run. That is the whole setup.
{"counties": ["Los Angeles"],"classifications": ["C10", "C36"],"activeOnly": true,"maxResults": 500}
| Input | Default | Notes |
|---|---|---|
licenseScope | all | all = 245 927 rows; master = 142 422; personnel = 155 031 |
counties | [] | case-insensitive, e.g. ["Orange"] |
classifications | [] | any-of matching, e.g. ["B", "C10"] |
activeOnly | true | keeps CLEAR — 234 021 of 245 927 |
statuses | [] | exact statuses, overrides the switch |
businessTypes | [] | cut sole proprietors out of a corporate list |
requiresPhone | false | removes about 240 records |
licenseNumbers | [] | filter while streaming, not a lookup |
updatedSinceDays | 0 | LastUpdate cursor for a change feed |
incremental | false | skip licence numbers already delivered for this exact query |
maxResults | 500 | hard cap, and the download stops when it is reached |
maxScanRows | 0 | stop before maxResults on a rare filter |
includeRaw | false | attach the untouched CSV row |
downloadTimeoutSecs | 900 | the full 77.7 MB file took 49 s when re-measured 2026-11-02 |
Reliability: the export is validated by its body, not by its MIME type
On 2026-11-02 CSLB started serving the identical CSV bytes as
application/vnd.ms-excel (it answered text/csv when measured on 2026-11-01),
still with content-disposition: attachment; filename=MasterLicenseData.csv and
the first line unchanged. An earlier build gated on the Content-Type and failed
every automated run in ~20 s without reading a single data row, which had the
Actor flagged Under maintenance.
The actor now decides from the response body: the first bytes must start with
LicenseNo and the header must match all 52 published columns. A MIME type is
only used to reject an HTML page, and an unexpected label on correct bytes is
logged (CSLB served the CSV body under a non-CSV Content-Type) instead of
failing the run. There is also a 240 s wall-clock ceiling on the stream, so a slow
day emits the records selected so far instead of being killed at the platform's
5-minute limit.
Caveat: do not trust the "master list"
The CSLB portal's own export button hands you a file called
MasterLicenseData with 142 422 rows. Measured 2026-11-01, that file is a
strict subset of the complete export:
master (142 422) ⊂ personnel (155 031) ⊂ all (245 927)
The 103 505 licences present only in the complete file include 98 808 in good
standing — that is 42 % of the active licences in the state. A lead list built
from the portal's own master file silently misses almost half the market, so
licenseScope defaults to all.
Caveat: closed licences are in the same file
Of 245 927 rows: CLEAR 234 021, then Contr Bond Susp, Work Comp Susp,
Liab Ins Susp, Family Sup Susp, Susp - No Qualifier, SOS Suspension,
EMP/WK Bnd Susp for the rest. activeOnly is on by default, so you get
licensed contractors rather than a list of everyone who was ever licensed.
Caveat: sourceUrl is not a permalink you can fetch
LicenseDetail.aspx rejects a plain GET with a 302 back to the search page;
the record only opens after a server-side name search. The URL is included so a
human can find the licence, not as a fetchable link. Verification means pasting
the licence number into the CSLB licence search.
Parsing details worth knowing
- Classifications are pipe-separated, not comma-separated:
C15| C54| D10→["C15", "C54", "D10"]. - Dates are
MM/DD/YYYY, and some optional ones carry a leading space (" 01/31/2025") — parsed, not padded intonull. - The CSV is real CSV: quotes around names with commas,
""escapes, and quoted newlines. It is parsed by a streaming state machine, not bysplit(','), and it survives a row split across two network chunks. - Column order is verified on every run. If CSLB renames or reorders a column, the actor fails loudly instead of writing a dataset full of nulls.
Cost
$2 per 1 000 licences, plus a one-off $0.00005 for starting the run.
Published in the Apify Store as
ai-lead-gen-tools/us-ca-cslb.
Rate this card shows: company-scraped = $0.002 per record (the primary event,
so the card advertises $2 / 1 000), apify-actor-start = $0.00005 — and the
start event is deliberately not primary, otherwise the card would advertise
$0.05 / 1 000 while billing per record.
You pay only for records that land in the dataset. The export is streamed and
aborted as soon as maxResults is reached, so a 500-record run reads a fraction
of the file instead of all 245 927 rows — the cap is a cost control, not just a
convenience.
Notes
- Data comes straight from
cslb.ca.gov; no proxy is used, so no proxy cost. - CSLB stamps
LastUpdateon every row: setupdatedSinceDaysto 7 and put the actor on a weekly schedule to get a change feed instead of a full re-download. - The same licence number never appears twice in the export, so
LicenseNois the dedup key. Dedup is opt-in viaincremental: with it on, a re-run of the same query continues further down the export instead of re-billing records you already have. With it off (the default) an identical input returns an identical result set, because the export is ordered by licence number and a reader that remembers past output walks down the file on every run — which looks like the dataset silently changing when you ask the same question twice.