California has more licensed contractors than any other state in the country - over 280,000 active licensees at any given time - and verifying them is genuinely complicated. The Contractors State License Board (CSLB) administers more than 50 separate license classifications covering everything from general building and engineering to swimming pools, insulation, and low-voltage systems. If your platform onboards contractors in California, you need to understand what you are looking at when a license comes back from the CSLB database.
This guide walks through the full process: the manual lookup on cslb.ca.gov, what every field in the result actually means, the common license classes you will encounter, how to read disciplinary history, and finally how to replace all of that with a single API call using ContractorVerify.
Why California Is the Most Complex State for Contractor Licensing
Most states have a single contractor license type with a handful of trade endorsements. California built an entirely different system. The CSLB issues licenses under three top-level classes - Class A, Class B, and Class C - where Class C alone has 42 specialty subcategories. This matters for platforms because a contractor might hold a C-10 Electrical license but not a C-20 HVAC license, and those are not interchangeable.
Beyond the classification complexity, California licenses attach to both individuals and business entities. A license can be held by a sole owner, a corporation, a partnership, or a joint venture. When you look up a license, the "qualifier" listed is the responsible managing employee (RME) or responsible managing officer (RMO) whose exam scores and trade experience back the license. If that qualifier is removed or dies, the business license goes on suspension within 90 days unless a new qualifier is added - so a company that looks licensed today could quietly lose its qualifier next month.
California also requires contractors to carry a surety bond (currently $25,000) and workers' compensation insurance if they have employees. Both are tracked in the CSLB database and both have independent expiration dates. A license can be Active but have lapsed insurance - which means the contractor is technically in violation even though the license itself shows green.
The Manual Verification Process: Step by Step
Step 1 - Go to the CSLB License Check Page
Navigate to cslb.ca.gov and find "Check a License" in the top navigation. The direct URL is cslb.ca.gov/OnlineServices/CheckLicenseII/CheckLicense.aspx. This is a public tool with no login required.
Step 2 - Search by License Number or Business Name
You can search by license number, business name, or qualifier name. License number is the fastest and most reliable - California CSLB numbers are 6 to 8 digits with no leading letters. Business name search often returns multiple results when the name is common, and it does not do fuzzy matching, so "ABC Plumbing" will not find "ABC Plumbing Inc."
If you only have a business name, start with the full legal name. If that fails, try dropping "Inc.", "LLC", or "Corp." suffix and searching again.
Step 3 - Read the License Detail Page
Once you select a result, you land on a detail page with multiple sections. Here is what each major field means:
| Field | What It Means | What to Watch For |
|---|---|---|
| License Number | Unique CSLB-issued identifier | Always record this - it is the stable key, names change |
| Issue Date | Date the license was first issued | Long-tenured licenses (10+ years) are generally lower risk |
| Reissue Date | Date of most recent renewal or reactivation | Gaps between issue and reissue can indicate suspensions |
| Expiration Date | License expires on this date unless renewed | CSLB sends renewal notices 60 days out; license is void day after expiry |
| Status | Active, Inactive, Suspended, or Cancelled | Only Active licenses are legally authorized to contract in CA |
| Classifications | One or more license class codes | Verify the classification matches the work being contracted |
| Qualifier Name | Individual whose exam/experience backs the license | If qualifier recently changed, verify the transition was clean |
| Bond Amount / Bond Company | Surety bond details ($25,000 required) | Check expiration - a lapsed bond can trigger CSLB action |
| Workers Comp | Insurer name and policy number | "Exempt" means the licensee has declared no employees |
The License Classification System Explained
California uses a three-tier classification hierarchy that confuses many platforms the first time they encounter it.
Class A - General Engineering Contractor
Class A licenses cover projects where the principal work is engineering in character - grading, clearing, paving, pipeline work, excavation, and similar. An A license holder does not automatically qualify to do building construction work. A large civil construction company might hold an A license without holding a B.
Class B - General Building Contractor
Class B is what most residential homeowners think of as "a contractor's license." A B license covers projects where two or more unrelated building trades are involved and the framing or structural elements are part of the scope. A general contractor who manages a full kitchen remodel (framing, drywall, plumbing rough-in, electrical) would typically carry a B.
Important nuance: a B license holder can sub out specialty work, but cannot perform specialty trades (like plumbing or electrical) as their primary work without also holding the relevant C license. This creates a common compliance gap on marketplace platforms where a general contractor takes on what is effectively a C-10 job without holding a C-10.
Class C - Specialty Contractors (42 categories)
The C classifications are where most verification complexity lives. Common ones you will encounter on home services platforms:
- C-10 - Electrical: any work on electrical systems, wiring, panels, fixtures
- C-20 - Warm-Air Heating, Ventilating and Air Conditioning (HVAC)
- C-36 - Plumbing: drain, waste, vent, water supply, gas lines
- C-27 - Landscaping: grading, irrigation, planting
- C-33 - Painting and Decorating
- C-35 - Lathing and Plastering
- C-39 - Roofing
- C-43 - Sheet Metal: ductwork, gutters, flashing
- C-15 - Flooring and Floor Covering
- C-61 - Limited Specialty: a catchall class for work not covered by other C classes
A contractor can hold multiple classifications on a single license number. Always check whether the classifications actually cover the job type - a C-27 landscaping contractor is not authorized to run a new irrigation backflow preventer that ties into the domestic water supply (that is C-34 Pipeline, or arguably C-36 Plumbing).
How to Read Disciplinary Actions
The CSLB license detail page includes a "Disciplinary Actions" section at the bottom. Most licenses show nothing here. When it is populated, you need to understand what you are reading.
The most serious outcomes are license revocations (the CSLB permanently ended the license) and license suspensions (temporarily pulled, usually for consumer complaints, citation non-payment, or criminal convictions related to contracting). Less serious are citations - these are administrative fines and orders, similar to traffic tickets for contractor violations. A single old citation from 2018 for a minor bookkeeping violation is very different from a 2024 suspension for consumer fraud.
Probationary status is particularly important to check. A contractor can have an "Active" license status but simultaneously be on probation with conditions attached - required supervision, mandatory reporting, or restrictions on certain work types. The probation details are listed in the disciplinary section and will not appear in the main status field.
The Problem with Manual Checks at Scale
The CSLB website works fine for one-off lookups. It breaks down entirely as a compliance workflow once you have more than a few dozen contractors to manage. The search interface does not have a bulk upload or CSV export. There is no API. Each lookup is a manual click-through that takes 2-3 minutes per contractor when you account for typing, page loads, reading results, and recording data.
At 500 contractors, that is 16-25 hours of work per verification cycle. And because licenses expire and status changes happen continuously, a one-time check at onboarding is not enough - you need to re-verify periodically. Platforms in the home services and construction space typically re-verify quarterly at minimum. That 16-25 hour task now happens four times a year.
Then there are the edge cases: contractors who hold licenses in multiple states, contractors whose business name changed after you onboarded them, licenses that technically renewed but with different classifications than when you first checked. Managing all of this manually means your compliance data is always somewhat stale and your team spends significant time on work that should be automated.
For a deeper look at how this breaks down organizationally, see our post on why manual license checks fail at scale.
Automating California License Verification with the ContractorVerify API
The ContractorVerify API exposes the CSLB database (and 49 other state boards) through a single consistent endpoint. You pass a contractor identifier and state code, get back a structured JSON response with all the fields that matter, and your system makes decisions automatically.
Making the API Call for California
The basic endpoint is:
GET https://api.contractorverify.io/v1/verify
?license_number=123456
&state=CA
&api_key=YOUR_KEY
You can also query by business name if you do not have the license number:
GET https://api.contractorverify.io/v1/verify
?name=Apex+Electrical+Services
&state=CA
&api_key=YOUR_KEY
Name-based lookups return a candidates array when there are multiple matches (common with generic business names). Your code should handle the multi-result case - typically by surfacing candidates to the user for confirmation, or by requiring the license number as a required field during onboarding to avoid ambiguity entirely.
Sample API Response for a California License
{
"license_number": "123456",
"state": "CA",
"status": "Active",
"business_name": "Apex Electrical Services Inc",
"qualifier_name": "John R. Doe",
"classifications": [
{ "code": "C-10", "description": "Electrical" },
{ "code": "C-7", "description": "Low Voltage Systems" }
],
"issue_date": "2011-04-15",
"expiration_date": "2027-02-28",
"bond": {
"amount": 25000,
"company": "Contractors Bonding and Insurance Co",
"expiration_date": "2026-12-31",
"status": "Active"
},
"workers_comp": {
"insurer": "State Compensation Insurance Fund",
"policy_number": "9876543",
"status": "Active"
},
"disciplinary_actions": [],
"verified_at": "2026-03-25T14:22:00Z",
"cache_ttl_seconds": 86400
}
How to Interpret the Response Fields
The status field will be one of: Active, Inactive, Suspended, or Cancelled. Your onboarding gate should only pass Active. Store the raw value in your database - do not reduce it to a boolean - because the distinction between Inactive and Suspended matters for how you communicate with the contractor.
The classifications array lets you validate that the license covers the work type in your system. If your platform has job categories mapped to CSLB classification codes, you can do this check programmatically at onboarding.
The expiration_date is what you will use for expiration monitoring - store it and set up alerts for 90, 30, and 7 days out. See our post on automating license expiration monitoring for the full implementation pattern.
The disciplinary_actions array is empty for clean licenses. When populated, each entry includes a type, date, and description. Your risk policy determines what triggers automatic rejection versus manual review - but having the data in a machine-readable format means you can apply that policy consistently at scale.
The cache_ttl_seconds: 86400 field indicates this response is backed by a 24-hour cache. The ContractorVerify API refreshes source data from CSLB nightly. For status-sensitive workflows - like approving payment on a job - you can pass force_refresh=true to bypass the cache and get a live fetch, though this counts as a higher-cost lookup.
Node.js Example: Verifying a Contractor During Onboarding
const verifyCALicense = async (licenseNumber) => {
const url = new URL('https://api.contractorverify.io/v1/verify');
url.searchParams.set('license_number', licenseNumber);
url.searchParams.set('state', 'CA');
url.searchParams.set('api_key', process.env.CONTRACTORVERIFY_KEY);
const res = await fetch(url.toString());
if (!res.ok) {
const err = await res.json().catch(() => ({}));
throw new Error(err.message || `Verification failed (${res.status})`);
}
const data = await res.json();
// Gate: must be Active
if (data.status !== 'Active') {
return {
approved: false,
reason: `License status is ${data.status}`
};
}
// Gate: must not be expired
const expiresAt = new Date(data.expiration_date);
if (expiresAt < new Date()) {
return { approved: false, reason: 'License is expired' };
}
// Gate: no active disciplinary actions
const activeDiscipline = data.disciplinary_actions
.filter(d => d.active === true);
if (activeDiscipline.length > 0) {
return { approved: false, reason: 'Active disciplinary action on record' };
}
return {
approved: true,
licenseData: data
};
};
When License Data Goes Stale
The CSLB database is not a real-time feed. The board updates records as renewals are processed, citations are issued, and disciplinary actions are finalized - but some of these updates have processing lag. A contractor who renewed their license online last Thursday might not show the updated expiration date on the CSLB website until Monday when the batch processing runs.
A 24-hour cache TTL on the ContractorVerify API side reflects this reality. Pulling fresh data more frequently than once a day does not actually improve data quality because the source is not updating that fast. The exception is immediate post-renewal situations, where a contractor just renewed and wants to verify their updated status for onboarding purposes - the force_refresh option exists for exactly this case.
For your own storage, the right pattern is to record last_verified_at alongside every license record and refresh on a scheduled cadence. If the expiration date is more than 90 days away, monthly re-verification is fine. If expiration is within 90 days, step up to weekly. If expiration is within 14 days, daily.
Handling Multi-State Contractors
California-based contractors who do work near state borders (Nevada, Arizona, Oregon) often hold licenses in multiple states. The ContractorVerify API treats each state as a separate lookup - there is no cross-state "contractor record." For multi-state contractors, you make one call per state and store the results under the same contractor ID in your database.
On the CSLB side specifically, note that California does not have reciprocity agreements with other states. A California B license does not translate to any standing in Nevada or Oregon. Each state license is independent and has to be verified independently.
For a full breakdown of what each state requires and which states have meaningful complexity, see our 2026 overview of contractor license requirements by state.
Summary
California contractor license verification is more complex than most states because the CSLB maintains 50+ license classifications, separates business license from qualifier individual records, and tracks bond and workers' comp status as independent data points. Manual lookups on cslb.ca.gov work for occasional checks but do not scale to anything resembling a real onboarding or compliance workflow.
The ContractorVerify API resolves all of this into a single call that returns structured, machine-readable data. You get status, classifications, expiration, bond status, workers' comp status, and disciplinary history in one response - and you can programmatically gate your onboarding flow on any combination of those fields.
If you are building compliance logic for a California-heavy contractor pool, start with the /v1/verify endpoint with state=CA, store the expiration_date and verified_at fields, and set up re-verification crons before those expiration dates arrive.