# Contractor License Search and New License Leads, 5 States (`dynamic_jingle/multi-state-contractor-license-search`) Actor

Search, verify and monitor contractor licenses in California, Texas, Washington, Oregon and Virginia from official state open data. One clean schema, weekly new-license lead feed, trade and status filters.

- **URL**: https://apify.com/dynamic_jingle/multi-state-contractor-license-search.md
- **Developed by:** [Scott Davidson](https://apify.com/dynamic_jingle) (community)
- **Categories:** Lead generation, Business
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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

## Contractor License Search and New License Leads (CA, TX, WA, OR, VA)

Search, verify, and monitor contractor licenses across **California, Texas, Washington, Oregon and Virginia** in one run. Every record comes from the state licensing board's own **official open data or bulk download**, and every state returns the **same normalized fields**. Turn on the **New licenses monitor** to get a weekly feed of newly licensed contractors, ready for Clay, HubSpot, Salesforce or any CRM.

**Price:** $3 per 1,000 license records, $10 per 1,000 new-license leads, plus $0.005 per run. You only pay for results you get.

### What this contractor license scraper does

- **Contractor license lookup and verification**: check a license number or business name and get status, expiration date, trade and address.
- **Bulk contractor lists by trade**: export every licensed roofer, electrician, plumber, HVAC contractor or general contractor in one or more states.
- **New contractor license leads**: schedule the monitor weekly and receive only licenses that are new since the last run.
- **One schema for five states**: no more stitching together five different state formats.

### Use cases

#### GTM and lead generation

Sell roofing materials, field-service software, payroll, trucks or insurance? Brand-new contractors are buying everything for the first time. Run the monitor every Monday with `tradeKeywords: ["roofing"]` and push each new license straight into Clay for enrichment and outreach. Every new license is a fresh, timely lead.

#### Insurance, surety and bonding

Find contractors whose licenses expire soon or whose bond or workers' comp status changed (California and Oregon include bond and insurance carrier data). Reach them before renewal.

#### Vetting and compliance

Marketplaces, general contractors, property managers and lenders can confirm a contractor's license is active and in the right trade before hiring, paying or approving them.

#### Market sizing and territory planning

Count licensed contractors per trade, county or ZIP code to size a market or plan sales territories.

### Supported states and sources

| State | Official source | Records | Issue date | Status |
|---|---|---|---|---|
| California | CSLB Public Data Portal, License Master list | ~245,000 current or renewable licenses | Yes | Yes (CLEAR, suspensions) |
| Texas | TDLR All Licenses on data.texas.gov (contractor license types only) | A/C, electrical, sign, appliance, elevator, well drilling contractors | No | Derived from expiration date |
| Washington | L\&I Contractor License Data on data.wa.gov | ~161,000 | Yes (effective date) | Yes |
| Oregon | CCB Active Licenses on data.oregon.gov | ~57,000 active | Yes (original registration) | Active only |
| Virginia | DPOR Class A, B and C contractor regulant lists | ~50,000 | Yes (certification date) | Current only |

This Actor never touches captcha-protected search pages. It reads the bulk files and open-data APIs that each state publishes for public use, and it respects each site's crawl rules.

### Input

| Field | Type | Description |
|---|---|---|
| `mode` | `lookup` or `newLicenses` | Lookup returns all matches. New licenses monitor returns only licenses not seen in earlier runs. |
| `states` | array | Any of `CA`, `TX`, `WA`, `OR`, `VA`. |
| `tradeKeywords` | array | Matches license type, class code or trade, for example `roofing`, `electrical`, `plumbing`, `HVAC`, `C39`. |
| `name` | string | Business, licensee or DBA name contains this text. |
| `licenseNumber` | string | Verify one license number. |
| `status` | string | `any`, `active`, `inactive`, `expired`, `suspended`. |
| `issuedAfter` | date | Only licenses issued on or after this date. |
| `maxResults` | integer | Total cap across all states. |
| `maxResultsPerState` | integer | Cap per state. |
| `monitorId` | string | Separate memory for each scheduled monitor. |

### Output fields

| Field | Description |
|---|---|
| `state` | Two-letter state code |
| `licenseNumber` | License number as issued by the state |
| `businessName` | Business or company name |
| `licenseeName` | Person: owner, qualifier, principal or responsible managing individual |
| `dbaName` | Doing-business-as name, when provided |
| `licenseType` | License type or classification in plain English |
| `licenseTypeCode` | The state's code, for example C39, CCC, RGC |
| `trades` | List of trades or specialties |
| `status` | Status exactly as reported by the state |
| `statusCategory` | Normalized: active, inactive, expired, suspended, other |
| `issueDate` | Original issue date (YYYY-MM-DD) |
| `expirationDate` | Expiration date (YYYY-MM-DD) |
| `address` | street, street2, city, state, zip, county |
| `phone` | Phone, when the state publishes it (CA, TX, WA, OR) |
| `email` | Email, when the state publishes it (VA) |
| `bond`, `insurance` | Bond and insurance carrier details (CA, OR) |
| `isNewLicense`, `firstSeenAt` | Set in monitor mode |
| `sourceName`, `sourceUrl` | Where the record came from |
| `scrapedAt` | When the record was collected |

#### Sample output

```json
{
  "state": "OR",
  "licenseNumber": "261748",
  "businessName": "JT ELITE CONSTRUCTION LLC",
  "licenseeName": "JACOB TORRES AYALA",
  "licenseType": "Residential General Contractor",
  "licenseTypeCode": "RGC",
  "status": "ACTIVE",
  "statusCategory": "active",
  "issueDate": "2026-05-14",
  "expirationDate": "2028-05-14",
  "address": { "street": "2742 26TH AVE", "city": "FOREST GROVE", "state": "OR", "zip": "97116", "county": "Washington" },
  "phone": "(971) 732-6113",
  "sourceUrl": "https://data.oregon.gov/resource/g77e-6bhs.json?license_number=261748"
}
```

### Examples

**Every new licensed roofer in California and Washington issued since September 1**

```json
{ "mode": "lookup", "states": ["CA", "WA"], "tradeKeywords": ["roofing"], "issuedAfter": "2026-09-16", "status": "active" }
```

**Weekly new electrical and HVAC contractor leads in all five states** (schedule it weekly)

```json
{ "mode": "newLicenses", "states": ["CA", "TX", "WA", "OR", "VA"], "tradeKeywords": ["electrical", "hvac", "air conditioning"], "monitorId": "electrical-hvac" }
```

**Verify one contractor license**

```json
{ "states": ["CA"], "licenseNumber": "1000013" }
```

### How the new licenses monitor works

The first run sets a baseline and returns licenses issued in the last 7 days (or since `issuedAfter`). Each later run returns only licenses that were not in the previous run, so a weekly schedule gives you a clean weekly lead list with no duplicates. Texas does not publish issue dates, so Texas returns nothing on the first monitor run and only truly new licenses after that. Use a different `monitorId` for each saved search.

### Pricing

Pay per event:

- **$0.005** per run start
- **$0.003** per license record in lookup mode ($3 per 1,000)
- **$0.01** per new-license lead in monitor mode ($10 per 1,000)

Set a maximum charge on your run and the Actor stops cleanly when it is reached.

### FAQ

**Is this data legal to use?** Yes. All data comes from public records that each state publishes as bulk downloads or open data. You are responsible for following marketing laws such as CAN-SPAM and TCPA when you contact people.

**How fresh is the data?** Washington updates daily, Oregon and California about daily, Virginia every 5 business days, Texas on the state's regular schedule. Each run downloads the latest files.

**Why are some fields empty?** States publish different fields. For example, only Virginia publishes email and only California and Oregon publish bond and insurance details. Missing values are `null`, never guessed.

**Does it include expired or revoked licenses?** California includes current and expired-but-renewable licenses. Oregon and Virginia publish current licenses only. Washington and Texas include inactive and expired records, which you can filter with `status`.

**Why is Texas limited to some trades?** Texas has no statewide general contractor or roofing license. Texas licenses A/C, electrical, sign, appliance, elevator and well drilling contractors through TDLR, and those are included. Plumbing is licensed by a separate board.

**Can I use it with Clay, Zapier, Make or an AI agent?** Yes. Use the Apify integrations, the API, or the Apify MCP server.

**Which states are next?** More states with official open data are coming. Tell us which states you need in the Issues tab.

# Actor input Schema

## `mode` (type: `string`):

What to return. Use "lookup" to search, verify or export every license matching your filters, for example all active roofers in California. Use "newLicenses" to get only licenses that are new since your last run, for example a weekly feed of newly licensed electricians. Schedule the monitor weekly for a lead list with no duplicates.

## `states` (type: `array`):

Two-letter codes of the states to search. Supported: CA, TX, WA, OR, VA. Example: \["CA", "WA"] to search only California and Washington.

## `tradeKeywords` (type: `array`):

Return licenses whose type, class code or trade contains any of these words (case-insensitive). Examples: "roofing", "electrical", "plumbing", "hvac", "air conditioning", "general", "solar", or state codes like "C39" (CA roofing), "RGC" (OR residential general), "ROC" (VA roofing). Leave empty for all trades.

## `name` (type: `string`):

Return only licenses whose business name, licensee name or DBA contains this text (case-insensitive). Example: "ROOFING" or "SMITH ELECTRIC". Lookup mode only.

## `licenseNumber` (type: `string`):

Verify one specific license by its number. Pair it with the state. Examples: "1000013" (CA), "261748" (OR), "ECOSTSC758NN" (WA). Lookup mode only.

## `status` (type: `string`):

Filter by normalized license status. Example: "active" for sales lead lists, "suspended" to find contractors with bond or insurance problems, "any" to return everything.

## `issuedAfter` (type: `string`):

Only return licenses issued on or after this date, in YYYY-MM-DD format. Example: "2026-09-01" for licenses issued since September 1. Texas does not publish issue dates, so Texas records are skipped when this is set in lookup mode. In monitor mode the first run defaults to the last 7 days.

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

Stop after this many results across all states. Use it to control cost. Example: 100 for a quick sample, 0 for no limit.

## `maxResultsPerState` (type: `integer`):

Cap the number of results from each state, so one large state does not use the whole budget. Example: 200. Use 0 for no per-state limit.

## `monitorId` (type: `string`):

Name for the monitor's memory of licenses already seen. Use a different ID for each saved search you schedule. Example: "ca-roofers-weekly" or "wa-or-electricians". Only used in newLicenses mode.

## Actor input object example

```json
{
  "mode": "lookup",
  "states": [
    "CA",
    "TX",
    "WA",
    "OR",
    "VA"
  ],
  "tradeKeywords": [
    "roofing"
  ],
  "status": "any",
  "maxResults": 1000,
  "maxResultsPerState": 0,
  "monitorId": "default"
}
```

# Actor output Schema

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

Contractor license records from the selected states.

## `summary` (type: `string`):

Per-state scan counts, emitted totals, and monitor baseline flags.

# 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 = {
    "tradeKeywords": [
        "roofing"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("dynamic_jingle/multi-state-contractor-license-search").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 = { "tradeKeywords": ["roofing"] }

# Run the Actor and wait for it to finish
run = client.actor("dynamic_jingle/multi-state-contractor-license-search").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 '{
  "tradeKeywords": [
    "roofing"
  ]
}' |
apify call dynamic_jingle/multi-state-contractor-license-search --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,dynamic_jingle/multi-state-contractor-license-search"
        }
    }
}
```

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/Kw2mGTQ9AmHSTwJbg/builds/V3MXXXdVheIzykYiF/openapi.json
