California Contractor License Search (CSLB) avatar

California Contractor License Search (CSLB)

Pricing

from $1.80 / 1,000 company scrapeds

Go to Apify Store
California Contractor License Search (CSLB)

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

Vladimir Chernykh

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

3 minutes ago

Last modified

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.

FieldSourceNotes
nameBusinessNamethe name printed on the wall, upper case
legalNameFullBusinessNamethe full legal form of the name
businessTypeBusinessTypeCorporation 70 763, Sole Owner 56 934, Limited Liability 12 720, Partnership 1 614, JointVenture 388
entityTypederived from BusinessTypeCORPORATION, SOLE PROPRIETOR, LLC, … — mapped, see below
documentNumberLicenseNoCalifornia licence number
phone (businessPhone, businessPhoneDigits, businessPhoneE164)BusinessPhone(925) 383 0487, 9253830487, +19253830487
countyCounty50 real counties
mailingAddresssix address columnsstreet1, city, state, zip, country; country is blank in the source and filled as United States
classificationsClassifications(s)trade classes, pipe-separated in the source
licenseStatusPrimaryStatusCLEAR, Contr Bond Susp, Work Comp Susp, …
isActivePrimaryStatustrue only for CLEAR
issueDate, expirationDate, inactivationDate, reactivationDate, lastUpdatefive date columnsISO 8601
workersComp, bondsWC + three bond blocksinsurer, policy, effective/expiry/cancellation dates
discipline, pending, asbestosRegistrationfour status columnsfalse on a clean licence
sourceUrlbuilt from LicenseNosee 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
}
InputDefaultNotes
licenseScopeallall = 245 927 rows; master = 142 422; personnel = 155 031
counties[]case-insensitive, e.g. ["Orange"]
classifications[]any-of matching, e.g. ["B", "C10"]
activeOnlytruekeeps CLEAR — 234 021 of 245 927
statuses[]exact statuses, overrides the switch
businessTypes[]cut sole proprietors out of a corporate list
requiresPhonefalseremoves about 240 records
licenseNumbers[]filter while streaming, not a lookup
updatedSinceDays0LastUpdate cursor for a change feed
incrementalfalseskip licence numbers already delivered for this exact query
maxResults500hard cap, and the download stops when it is reached
maxScanRows0stop before maxResults on a rare filter
includeRawfalseattach the untouched CSV row
downloadTimeoutSecs900the 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.

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 into null.
  • The CSV is real CSV: quotes around names with commas, "" escapes, and quoted newlines. It is parsed by a streaming state machine, not by split(','), 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 LastUpdate on every row: set updatedSinceDays to 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 LicenseNo is the dedup key. Dedup is opt-in via incremental: 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.