# Contractor License Lookup & Verification: Bulk, Multi-State (`reliable_tools/contractor-license-lookup`) Actor

Contractor license lookup for many US states in one run. Verify license numbers or find a contractor by business name: status, classifications, expiry, bonds, insurance and an AI match score.

- **URL**: https://apify.com/reliable_tools/contractor-license-lookup.md
- **Developed by:** [Reliable Tools](https://apify.com/reliable_tools) (community)
- **Categories:** Lead generation, AI
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.40 / 1,000 license records

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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 Lookup & Verification: Bulk, Multi-State

This contractor license lookup checks contractor licenses in 14 US states in one run, from the official open data
of each state licensing board. Give a list of license numbers, business names, or both. Every row comes back in the
same schema: license holder, DBA, a clean status (active, expired, suspended, revoked or inactive), license type and
classifications, issue and expiry dates, bonds, insurance and disciplinary flags where the state publishes them, the
business address, the data date and a link to the state's own lookup page. When you search by business name, you also
get an AI match score that tells you if the license holder is the same business as the one in your list.

### What does Contractor License Lookup do?

State license lookup pages check one contractor at a time, each state looks different, and most of them sit behind a
captcha. This Actor answers three jobs in bulk:

1. **Verify license numbers.** Paste hundreds of numbers with their state. You see who holds each license and whether
   it is active today, plus expiry, bond and insurance details where published.
2. **Find the license of a contractor by name.** Give the business name and state (city or ZIP help). The Actor finds
   the closest license holders and scores the best one.
3. **Check that a license belongs to the business you deal with.** Give the number and the name. The Actor looks up
   the license and tells you if the holder is the same business, a related one or a different one.

Licenses that are not found, states that are not covered yet and invalid rows are free.

### Who uses it

- Insurers and surety companies checking contractor status before binding or renewing a policy or bond
- Lenders, property managers and home warranty companies vetting contractors on a job
- Home service marketplaces and lead platforms verifying every contractor before listing them
- General contractors checking subcontractors, and procurement teams screening bidders
- Compliance teams that need a dated record of the check

### States and sources

| State | Licensing board and source | What is covered | Licence of the data |
|---|---|---|---|
| CA | Contractors State License Board, CSLB Public Data Portal License Master file | All renewed and renewable contractor licenses, with bonds, workers' comp and disciplinary bond flags | California public information (ca.gov use policy: public domain unless indicated) |
| WA | Labor & Industries, data.wa.gov contractor, bond and insurance datasets | Construction, electrical, plumbing and elevator contractors, all statuses, bonds and liability insurance | Open Data Commons PDDL |
| TX | Department of Licensing and Regulation, data.texas.gov All Licenses | A/C, electrical, sign, appliance, elevator and water well licenses (contractors and licensed trades) | Texas open data, no reuse restriction stated |
| OR | Construction Contractors Board and Building Codes Division, data.oregon.gov | Active CCB licenses with bond, insurance and workers' comp exemption; electrical, plumbing, boiler and elevator contractors | Public domain |
| MN | Department of Labor and Industry license export files | Residential contractors, contractor registrations, plumbing and electrical businesses, enforcement flag | Minnesota public government data |
| VA | DPOR Board for Contractors regulant lists | Current Class A, B and C contractors with specialties | Free public lists, no reuse restriction stated |
| OH | Construction Industry Licensing Board roster (Ohio eLicense) | Active commercial electrical, HVAC, hydronics, plumbing and refrigeration contractors | Ohio public records |
| CT | Department of Consumer Protection, data.ct.gov | Home improvement, new home construction, major contractor and every trade contractor credential, all statuses | Public domain |
| CO | DORA, data.colorado.gov | Electrical, plumbing and water conditioning contractors, all statuses, discipline cases | Public domain |
| NY | NYS Department of Labor (data.ny.gov) and New York City DOB and DCWP (NYC Open Data) | NYS contractor registry, elevator and mold contractors; NYC general, electrical, plumbing and home improvement contractors | NYS open data terms; NYC Open Data |
| IL | IDFPR, data.illinois.gov | Licensed roofing contractors, all statuses, discipline | ODbL 1.0, https://opendatacommons.org/licenses/odbl/1-0/ (attribution: Illinois IDFPR). Derived rows are made available under ODbL 1.0 |
| IA | Division of Labor, data.iowa.gov | Active construction contractor registrations | CC BY 4.0, https://creativecommons.org/licenses/by/4.0/ (attribution: State of Iowa). Modified: fields renamed and normalised |
| DE | Division of Professional Regulation, data.delaware.gov | Electricians, plumbers and HVACR licenses (individuals), discipline flag | Public domain |
| VT | Division of Fire Safety, data.vermont.gov | Electricians, plumbers, gas and oil installers (individuals) | ODbL 1.0, https://opendatacommons.org/licenses/odbl/1-0/ (attribution: Vermont Division of Fire Safety). Derived rows are made available under ODbL 1.0 |

Every row carries `sourceName`, `sourceUrl`, `sourceLicence` and `dataDate`, so you always know where a value came from
and how fresh it is. Bulk files are cached and refreshed every one to three days, in line with how often each state
updates them. API states are read live, with at least one second between two requests to the same open-data portal.

**Cache in your Apify account:** the state bulk files (CA, MN, VA, IA, OH) are stored as compressed databases in a named
key-value store called `rt-contractor-license-verifier-cache` in your own Apify account, so later runs do not download
them again. Each state file is rebuilt when it is older than one to three days. The store counts towards your Apify
storage, and it contains the full state files as published, including sole owners and individual tradespeople. You can
delete it at any time under Storage, Key-value stores in Apify Console; the next run builds it again.

**Coming:** Florida and Arizona publish files but behind a bot challenge, which this Actor does not bypass. Utah sells
its lists. Louisiana, North Carolina, Massachusetts, Maryland and Nevada offer lookup pages only. Pennsylvania has no
statewide contractor records in open data. Rows for these states come back as `unsupported_state` and are free. We add
a state as soon as its board publishes open data.

### What data you get

| Field | What it is |
|---|---|
| `result` | `found`, `not_found`, `unsupported_state`, `invalid_input` or `source_error` |
| `state`, `licenseNumber` | State and license number as the state writes it |
| `businessName`, `dba`, `otherNames` | License holder (legal or business name) and trade names |
| `status`, `isActive`, `statusDetail` | Clean status: `active`, `expired`, `suspended`, `revoked` or `inactive`, plus the state's own wording |
| `licenseType`, `classifications` | License type and classifications or specialties, with descriptions where the state defines them |
| `entityType` | Sole owner, corporation, LLC and so on, where published |
| `issueDate`, `expirationDate`, `lastUpdated` | Dates in YYYY-MM-DD |
| `bonds` | Surety, bond number, amount and dates (CA, WA, OR) |
| `workersComp` | Coverage, insurer, policy and dates (CA), exemption (OR), coverage flag (NY registry) |
| `liabilityInsurance` | Insurer, policy, amount and dates (WA, OR) |
| `disciplinaryAction` | `flag` plus details where the state publishes them (CA, MN, CO, IL, DE, NY registry); `null` when the state does not publish it |
| `address` | Address of record as the state publishes it. For sole owners in some states (for example Iowa, Virginia and California) this can be a street address |
| `stateBusinessId` | State business ID where published (WA UBI) |
| `match` | For name rows: `verdict`, `sameBusinessProbability`, a level score and the evidence (name similarity, city, ZIP) |
| `otherCandidates` | Up to three other close license holders, so you can pick by hand |
| `verifyUrl` | The state's own lookup page for a manual second look |
| `summary` | One sentence for a quick review |

Fields a state does not publish stay `null`. Nothing is guessed.

### How to verify contractor licenses step by step

1. Open the Actor and put your list into **Contractors to check**, one object per contractor.
2. Or paste `STATE NUMBER` lines into **Quick list**, for example `CA 1000046`, or link a CSV file.
3. Click **Start**. The sample input finishes in under a minute when the state files are cached.
4. Download the results as JSON, CSV or Excel, or read them through the Apify API.

```json
[
  {"reference": "C-1", "state": "CA", "licenseNumber": "1000046", "businessName": "Bayside Electric"},
  {"reference": "C-2", "state": "WA", "businessName": "04 Construction LLC", "city": "Vancouver"},
  {"reference": "C-3", "state": "TX", "licenseNumber": "TECL 25230"}
]
```

### Example output

```json
{
  "reference": "C-1",
  "result": "found",
  "state": "CA",
  "licenseNumber": "1000046",
  "businessName": "M TABBERT INC",
  "dba": "BAYSIDE ELECTRIC",
  "status": "active",
  "isActive": true,
  "statusDetail": "CLEAR",
  "licenseType": "Contractor license",
  "classifications": [{"code": "C-10", "description": "Electrical Contractor"}],
  "entityType": "Corporation",
  "issueDate": "2015-01-10",
  "expirationDate": "2027-08-31",
  "bonds": [{"type": "contractor's bond", "surety": "AMERICAN CONTRACTORS INDEMNITY COMPANY", "bondNumber": "100350527", "amountUsd": 25000, "effectiveDate": "2023-01-01", "cancellationDate": null}],
  "workersComp": {"coverage": "Workers' Compensation Insurance", "insurer": "OMAHA NATIONAL CASUALTY COMPANY", "policyNumber": "ONCC1701425401", "effectiveDate": "2026-07-06", "expirationDate": "2027-07-06"},
  "disciplinaryAction": {"flag": false, "details": []},
  "address": {"street": "131 SUNSET AVE STE E-337", "city": "SUISUN CITY", "state": "CA", "zip": "94585", "county": "Solano"},
  "match": {"verdict": "same", "sameBusinessProbability": 0.92, "evidence": {"nameSimilarity0to100": 100, "city": "unknown", "zip": "unknown", "score0to100": 70}},
  "sourceName": "CSLB Public Data Portal, License Master file",
  "verifyUrl": "https://www.cslb.ca.gov/OnlineServices/CheckLicenseII/LicenseDetail.aspx?LicNum=1000046",
  "dataDate": "2026-10-28",
  "summary": "CA license 1000046: M TABBERT INC (DBA BAYSIDE ELECTRIC), active, expires 2027-08-31. Same business as your record (probability 0.92)."
}
```

### How the business match works

1. Code compares your row with each license holder found: name similarity after removing legal forms (Inc, LLC,
   Corp), across the legal name, DBA and other names, plus city and ZIP.
2. An AI judge looks at both records and the code checks, and returns a calibrated probability that it is the same
   license holder, plus a level on a three-step scale.
3. Fixed rules in code turn that into the verdict:

| Verdict | Meaning |
|---|---|
| `same` | The license belongs to the business in your list |
| `uncertain` | Could be your business; a person should look |
| `related` | Same owner or business family, but a different license holder |
| `different` | The license belongs to someone else |

Sharing only a trade word such as "construction" or "electric" is not enough for `same`. Switch **AI match score** off
to get the name similarity only; those rows are billed as plain license records.

### Use it through the API

```bash
curl -X POST "https://api.apify.com/v2/acts/reliable_tools~contractor-license-lookup/run-sync-get-dataset-items?token=YOUR_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"contractors": [{"state": "CA", "licenseNumber": "1000046"}, {"state": "OR", "businessName": "Acocella Tile Inc"}]}'
```

The run summary (counts per result, status and verdict, and the data date of each state) is in the key-value store
record `SUMMARY`.

### Pricing

Pay per result, no subscription:

| Event | Price | When |
|---|---|---|
| License record | $0.004 | A license found by its number |
| License record with name match | $0.006 | A row with a business name: the best license record plus the AI match score (instead of the license record price, not on top) |
| Actor start | $0.00005 | Once per run |

Not found, unsupported state, invalid input and source errors are free. 1,000 verified licenses cost about $4.

### FAQ

**Where does the data come from?** Only from open data that the state boards publish for download or through their open-data APIs, under the licences listed in the sources table. The Actor never scrapes state lookup pages, never solves captchas and never logs in. This is not legal advice.

**Can I use it to screen people?** No. The output is not a consumer report under the US Fair Credit Reporting Act. Do not use it for decisions about employment, consumer credit, personal insurance or tenancy.

**Colorado data notice:** The data made available here has been modified for use from its original source, which is the State of Colorado. THE STATE OF COLORADO MAKES NO REPRESENTATIONS OR WARRANTY AS TO THE COMPLETENESS, ACCURACY, TIMELINESS, OR CONTENT OF ANY DATA MADE AVAILABLE THROUGH THIS SITE. THE STATE OF COLORADO EXPRESSLY DISCLAIMS ALL WARRANTIES, WHETHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. The data is subject to change as modifications and updates are complete. It is understood that the information contained in the Web feed is being used at one's own risk.

Reliable Tools is not affiliated with, endorsed or authorised by any state licensing board named here.

**How current is the data?** As current as each state's open data. Each board refreshes its files or datasets on its
own cycle, from daily to weekly. The `dataDate` field shows
the date of the data behind every row. For a legal decision, confirm on the state page in `verifyUrl`.

**Why is a California license not found?** The CSLB master file contains renewed licenses and expired licenses that can
still be renewed. Cancelled, revoked and long-expired licenses are not in it, so those come back as `not_found` with a
note, and you are not charged.

**Why does a Texas number show an electrician and not a contractor?** Texas reuses numbers across license types. The
Actor shows the contractor license first. Write the number with its prefix (`TECL 12345`, `TACLA12345C`) or add
`licenseType` to pick one.

**Does it show workers' comp and bonds for every state?** Only where the state publishes them in open data: California
(bond and workers' comp), Washington (bond and liability insurance), Oregon (bond, liability insurance and workers' comp
exemption) and the New York contractor registry (workers' comp flag). Other states return `null` for these fields.

**Can I check thousands of contractors?** Yes. License numbers are looked up in batches. For very large Socrata-state
lists you can add a free open-data app token.

**Something broke or a field is missing?** Open an issue on the Issues tab and we'll look at it.

### About Reliable Tools

This Actor is made by [Reliable Tools](https://apify.com/reliable_tools). We build tools that return clean, official data
plus an AI score with a probability, so you can filter and act on the results straight away. Our focus is compliance,
verification and B2B research.

If a field is missing or a license does not come back the way you expect, open an issue on the Issues tab. We read
every one and usually reply within a day. You can find our other Actors on our [profile page](https://apify.com/reliable_tools).

# Actor input Schema

## `contractors` (type: `array`):

One object per contractor. Fields: "state" (two letters, required), "licenseNumber" and/or "businessName", optional "city", "zip", "licenseType" and your own "reference". With a license number you get that license. With only a name, the closest license holder in that state is found and scored. With both, the license is checked against the name. The prefill is sample data.

## `licenses` (type: `array`):

Optional shortcut, one per line: state code and license number, for example "CA 1000002" or "WA ECOSTSC758NN".

## `csvUrl` (type: `string`):

Optional: a link to a CSV file with a header row. Recognised columns: state, licenseNumber (or license, licenseNo), businessName (or name, company), city, zip, licenseType, reference.

## `nameMatch` (type: `boolean`):

When a row has a business name, says whether the state license holder is the same business, a related one or a different one, with a probability. Off: you still get the name similarity score, and the row is billed as a license record.

## `socrataAppToken` (type: `string`):

Optional free app token for state open-data portals (Socrata). Only needed for very large lists; without it the public rate limit applies.

## `maxRows` (type: `integer`):

Stops after this many input rows.

## Actor input object example

```json
{
  "contractors": [
    {
      "reference": "C-1",
      "state": "OR",
      "licenseNumber": "109242"
    },
    {
      "reference": "C-2",
      "state": "WA",
      "businessName": "04 Construction LLC",
      "city": "Vancouver"
    },
    {
      "reference": "C-3",
      "state": "TX",
      "licenseNumber": "TECL 25230"
    }
  ],
  "nameMatch": true,
  "maxRows": 10000
}
```

# Actor output Schema

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

Dataset in the licenses view.

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

Counts per result, status and match verdict, with the data date of each state.

# 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 = {
    "contractors": [
        {
            "reference": "C-1",
            "state": "OR",
            "licenseNumber": "109242"
        },
        {
            "reference": "C-2",
            "state": "WA",
            "businessName": "04 Construction LLC",
            "city": "Vancouver"
        },
        {
            "reference": "C-3",
            "state": "TX",
            "licenseNumber": "TECL 25230"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("reliable_tools/contractor-license-lookup").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 = { "contractors": [
        {
            "reference": "C-1",
            "state": "OR",
            "licenseNumber": "109242",
        },
        {
            "reference": "C-2",
            "state": "WA",
            "businessName": "04 Construction LLC",
            "city": "Vancouver",
        },
        {
            "reference": "C-3",
            "state": "TX",
            "licenseNumber": "TECL 25230",
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("reliable_tools/contractor-license-lookup").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 '{
  "contractors": [
    {
      "reference": "C-1",
      "state": "OR",
      "licenseNumber": "109242"
    },
    {
      "reference": "C-2",
      "state": "WA",
      "businessName": "04 Construction LLC",
      "city": "Vancouver"
    },
    {
      "reference": "C-3",
      "state": "TX",
      "licenseNumber": "TECL 25230"
    }
  ]
}' |
apify call reliable_tools/contractor-license-lookup --silent --output-dataset

```

## MCP server setup

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

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/JAj3luc6uo7tuVrRV/builds/8r1MDB3ickiJTLfwm/openapi.json
