# 🏗️ California CSLB Contractor Scraper — Licenses & Leads (`zapticx/california-cslb-contractor-scraper`) Actor

Find and verify California CSLB contractors by license, classification, county, city, or ZIP. Export license status, phone, bonds, workers’ compensation, and personnel.

- **URL**: https://apify.com/zapticx/california-cslb-contractor-scraper.md
- **Developed by:** [Zapticx](https://apify.com/zapticx) (community)
- **Categories:** Lead generation, Developer tools, Automation
- **Stats:** 3 total users, 2 monthly users, 76.9% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.00 / 1,000 contractor results

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## 🏗️ California CSLB Contractor Scraper — Licenses & Leads

Find licensed contractors in California and export clean CSLB license records by classification, county, city, ZIP code, exact business name, or license number. This California contractor scraper combines official bulk CSLB data with optional live California contractor license lookup, giving you a low-cost contractor database for leads, compliance research, bond checks, and workers’ compensation review.

Basic results cost **$0.002 per unique contractor record ($2.00 per 1,000)** under the configured pay-per-event model. Enabling personnel enrichment adds **$0.015 once per run**, never per person. One dataset row represents one CSLB license; classifications, bonds, workers’ compensation, and personnel stay nested instead of creating duplicate billable rows.

Data comes from the official [CSLB Public Data Portal](https://www.cslb.ca.gov/OnlineServices/DataPortal/) and, when requested, the official live license-detail page.

CSLB does not provide contractor email addresses, so this Actor does not return fabricated emails.

### What can you do with this Actor?

- Build California contractor leads by trade and geography.
- Verify a list of CSLB license numbers and refresh current status/date fields.
- Find electrical, plumbing, roofing, HVAC, landscaping, solar, and general contractors.
- Screen subcontractors using license standing, expiration, bond, and workers’ compensation data.
- Research contractor classifications, counties, cities, and ZIP codes.
- Export structured records through the Apify API for analytics or automation.

### How to use

1. [Create a free Apify account](https://console.apify.com/sign-up). The current Free plan includes $5 of monthly usage credit.
2. Open the Actor, choose classifications or another meaningful filter, and set `maxResults`.
3. Enable bonds, workers’ compensation, personnel, or live verification as needed.
4. Run the Actor and export the default dataset as JSON, CSV, Excel, or another supported format.

The default C-36 + Los Angeles example returns 10 real results and is designed to finish within the Store’s five-minute expectation under observed source conditions.

### Features

- Official statewide CSLB bulk source; no page-per-contractor discovery crawl.
- Direct license-number lookup and case-insensitive exact business-name matching.
- Classification, county, city, ZIP, and license-status filtering.
- AND between filter categories and OR within each array.
- One normalized record per license number, with classification aggregation and deduplication.
- Structured contractor, workers/employee, and disciplinary bond arrays.
- Current/most-recent workers’ compensation fields as supplied by CSLB.
- Optional current personnel associations without one paid result per person.
- Optional direct-HTTP live verification; bulk data is retained if verification fails.
- Source dates, record dates, provenance URLs, concise run statistics, and schema-change detection.
- A validated cross-run cache that serves the last-known-good CSLB snapshot during source refreshes or temporary upstream failures. Output keeps the actual snapshot date in `sourceUpdatedAt`, and live verification remains available when current status matters.
- No residential proxy, browser automation, login, or email inference.

### Data fields

| Field | Description |
|---|---|
| `licenseNumber` | CSLB contractor license number as a string |
| `businessName`, `fullBusinessName`, `dba` | Legal/full business and doing-business-as names where supplied |
| `businessType` | CSLB entity/business type |
| `licenseStatus` | Derived `ACTIVE`, `INACTIVE`, `EXPIRED`, `SUSPENDED`, or `UNKNOWN` |
| `statusCode`, `secondaryStatus` | Source standing/status values retained for fidelity |
| `issueDate`, `reissueDate`, `expirationDate` | ISO dates when confidently parseable |
| `address` | Street, city, state, ZIP, county, and country |
| `phone` | Digits-only phone string |
| `classifications[]` | Display code, raw source code, and official name where mapped |
| `bonds[]` | Bond type, surety, number, amount, dates, and disciplinary metadata where present |
| `workersComp[]` | Coverage type, insurer, policy, and relevant dates |
| `personnel[]` | Current public associations, titles, classifications, dates, and qualifier bond where present |
| `personnelSourceUpdatedAt`, `personnelSourceState` | Optional personnel-snapshot date and cache state when enrichment is requested |
| `sourceUpdatedAt`, `recordUpdatedAt` | Bulk-source and record freshness |
| `verifiedLive`, `verifiedAt` | Live verification outcome and time |
| `sourceUrl` | Official CSLB license-detail URL |

`null` means CSLB did not supply a usable value. Empty arrays mean the related collection was not present or not requested.

### Input

- `searchMode`: `auto` (default), `master`, or identifier-oriented `lookup`.
- `licenseNumbers`: 1–8 digit license numbers. Identifier searches take precedence over discovery filters.
- `businessNames`: case-insensitive exact matches; fuzzy matching is not claimed.
- `classifications`: codes such as `A`, `B`, `B-2`, `C-10`, `C-36`, or `C-61/D-49`.
- `counties`, `cities`, `zipCodes`: geography filters.
- `licenseStatuses`: friendly status values.
- `includeBond`, `includeWorkersComp`: related-data controls included in the contractor-result price.
- `includePersonnel`: attach personnel and charge one $0.015 `personnel-enrichment` event for the run.
- `verifyLive`: verify selected licenses against current CSLB pages.
- `maxResults`: 1–10,000 unique license records.

```json
{
  "classifications": ["C-36"],
  "counties": ["Los Angeles"],
  "licenseStatuses": ["ACTIVE"],
  "includeBond": true,
  "includeWorkersComp": true,
  "includePersonnel": false,
  "verifyLive": false,
  "maxResults": 1000
}
```

If identifiers and discovery filters are both supplied, identifiers win and the Actor logs that discovery filters were ignored.

### Output

Illustrative structure (values are sanitized, not represented as a real contractor):

```json
{
  "licenseNumber": "1234567",
  "businessName": "EXAMPLE CONSTRUCTION INC",
  "dba": null,
  "businessType": "Corporation",
  "licenseStatus": "ACTIVE",
  "statusCode": "CLEAR",
  "issueDate": "2018-01-10",
  "expirationDate": "2027-01-31",
  "address": {
    "street": "123 MAIN ST",
    "city": "LOS ANGELES",
    "state": "CA",
    "zip": "90001",
    "county": "Los Angeles",
    "country": "US"
  },
  "phone": "5551234567",
  "classifications": [{ "code": "B", "rawCode": "B", "name": "General Building Contractor" }],
  "bonds": [{ "type": "contractor", "suretyCompany": "EXAMPLE SURETY", "bondNumber": "EXAMPLE-1", "amount": 25000, "effectiveDate": "2026-01-01", "cancellationDate": null }],
  "workersComp": [],
  "personnel": [],
  "dataSource": "CSLB",
  "sourceMode": "master",
  "sourceUpdatedAt": "2026-10-05",
  "verifiedLive": false,
  "verifiedAt": null,
  "sourceUrl": "https://www.cslb.ca.gov/1234567"
}
```

### Pricing

The PPE configuration has two events:

- `contractor-result` — **$0.002**, fired exactly once for each unique contractor record successfully written.
- `personnel-enrichment` — **$0.015**, fired exactly once before the larger personnel acquisition when `includePersonnel` is `true`. It is not charged when `includePersonnel` is `false`, and it never varies with the number of personnel attached.

| Records | Basic run | With personnel |
|---:|---:|---:|
| 100 | $0.200 | $0.215 |
| 1,000 | $2.000 | $2.015 |
| 5,000 | $10.000 | $10.015 |
| 10,000 | $20.000 | $20.015 |

Bonds and workers’ compensation add no surcharge. Personnel remains nested rather than creating paid rows, but requesting its expensive related-file scan adds the single $0.015 run charge even if the optional source ultimately returns no associations or cannot be acquired. The charge is attempted before that work so Apify spending limits can stop it. Live verification remains included in the contractor-result price for V1. See the measured and estimated [economics report](docs/ECONOMICS.md).

### Examples / Tasks

- **Los Angeles Plumbing Contractors** — C-36 + Los Angeles County + active status.
- **California Electrical Contractor Leads** — C-10 statewide, up to 5,000 records.
- **Roofing Contractors with Insurance Data** — C-39 across Los Angeles, Orange, and San Diego counties.
- **Verify a License List** — license numbers + `searchMode: "lookup"` + `verifyLive: true`.

Ready-to-copy inputs are in the [`examples`](examples/) directory.

### API & integrations

Run the Actor through the Apify API or `ApifyClient` for JavaScript/Python, then read the default dataset. You can also trigger runs with schedules or webhooks and connect the output through Apify’s Make and Zapier integrations. n8n, Google Sheets workflows, MCP clients, and AI agents can call the API or consume exported dataset data; availability depends on how you configure those external tools.

```js
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('ZAPTICX_ACTOR_ID').call({
  classifications: ['C-10'],
  counties: ['San Diego'],
  maxResults: 100,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
```

### Limitations

- CSLB bulk data is periodic; license status can change after a file is published. Use live verification for consequential checks.
- During a new CSLB publication or temporary portal outage, discovery may use the last validated snapshot instead of failing. The precise snapshot date remains visible in `sourceUpdatedAt` and the run log identifies stale-cache use.
- CSLB does not provide contractor email addresses. This Actor does not guess or synthesize them.
- Some source fields are legitimately absent, and personnel reflects public associations supplied by CSLB.
- Exact business-name matching is supported; fuzzy business search is not.
- Personnel adds a large source download and may materially extend runtime.
- Source availability and CSLB’s documented weekly maintenance window can affect runs.

### About Zapticx

Zapticx builds structured, automation-ready public-data products on Apify.

This Actor is unofficial and is not affiliated with or endorsed by the California Contractors State License Board (CSLB). It processes publicly available licensing information. Users are responsible for complying with applicable laws and source terms.

# Actor input Schema

## `searchMode` (type: `string`):

Auto uses the efficient statewide bulk source and adds live verification when requested. Lookup is intended for identifier searches and implies live verification.

## `licenseNumbers` (type: `array`):

One or more numeric CSLB license numbers (up to 8 digits). When provided, identifier matching takes precedence over discovery filters.

## `businessNames` (type: `array`):

Business-name lookup uses case-insensitive exact CSLB name matching. This is not fuzzy or Google-style search.

## `classifications` (type: `array`):

Official CSLB codes. Common examples: B — General Building; C-10 — Electrical; C-20 — Warm-Air Heating, Ventilating and Air-Conditioning; C-27 — Landscaping; C-36 — Plumbing; C-39 — Roofing. Multiple values use OR semantics.

## `counties` (type: `array`):

Exact California county names. Multiple counties use OR semantics.

## `cities` (type: `array`):

Case-insensitive exact city matches; nearby cities are not included. Multiple values use OR semantics.

## `zipCodes` (type: `array`):

Five-digit California ZIP codes or ZIP+4 values, preserved as strings.

## `licenseStatuses` (type: `array`):

Friendly status derived from CSLB standing, expiration, and inactivation fields. Multiple values use OR semantics.

## `includeBond` (type: `boolean`):

Attach available contractor, workers/employee, and disciplinary bond records.

## `includeWorkersComp` (type: `boolean`):

Attach current or most recent CSLB workers’ compensation records.

## `includePersonnel` (type: `boolean`):

Attach current publicly associated personnel. This requires a larger related-file scan and adds one $0.015 personnel-enrichment charge per run, never per person.

## `verifyLive` (type: `boolean`):

Check each selected license against the current CSLB license-detail page. Bulk results are retained if optional verification fails.

## `maxResults` (type: `integer`):

Maximum unique contractor/license records to return.

## Actor input object example

```json
{
  "searchMode": "auto",
  "licenseNumbers": [],
  "businessNames": [],
  "classifications": [
    "C-36"
  ],
  "counties": [],
  "cities": [
    "Los Angeles"
  ],
  "zipCodes": [],
  "licenseStatuses": [],
  "includeBond": true,
  "includeWorkersComp": true,
  "includePersonnel": false,
  "verifyLive": false,
  "maxResults": 10
}
```

# Actor output Schema

## `results` (type: `string`):

No description

## `overview` (type: `string`):

No description

## `runStats` (type: `string`):

No description

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {};

// Run the Actor and wait for it to finish
const run = await client.actor("zapticx/california-cslb-contractor-scraper").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {}

# Run the Actor and wait for it to finish
run = client.actor("zapticx/california-cslb-contractor-scraper").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{}' |
apify call zapticx/california-cslb-contractor-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,zapticx/california-cslb-contractor-scraper"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/q0oqGad165u4H0L75/builds/EvG3d4Wf0UdEQNnKB/openapi.json
