# US License Verification (Contractors & Professionals) (`maki-dev/us-license-verify`) Actor

Verify & normalize US professional and contractor licenses across 11 states (CO, TX, WA, OR, IL, NY, CT, DE, AR, VA, VT) into one clean schema: number, status, classification, bond, expiration, disciplinary flag.

- **URL**: https://apify.com/maki-dev/us-license-verify.md
- **Developed by:** [Mario Barrios](https://apify.com/maki-dev) (community)
- **Categories:** Developer tools, Lead generation
- **Stats:** 3 total users, 1 monthly users, 100.0% runs succeeded, 1 bookmarks
- **User rating**: No ratings yet

## Pricing

from $30.00 / 1,000 license verifieds

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/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.
Actors are written with capital "A".

## 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.
The best way to integrate Actors is as follows.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).

# README

## US License Verification API — Contractors & Professionals (11 States)

**Verify and normalize US professional & contractor licenses across 11 states into one clean, consistent schema.** Send a license number (or a name) for any supported state and get back a normalized record — status, classification, expiration, bond, and a disciplinary flag — straight from the official state licensing-board public records, with a source link and verification timestamp on every result.

A single **contractor license verification API** and **license lookup API** for bulk checks, KYB / vendor onboarding, and compliance monitoring. Stop writing and maintaining a different scraper for every state board. One input shape, one output shape, eleven states — built for lenders, sureties, insurers, vendor-onboarding and compliance teams, contractor lead-gen, and AI agents that need a fast, auditable license check.

### Why this Actor

- **One normalized schema across every state.** `licenseNumber`, `licenseeName`, `status` (active / inactive / expired / suspended / revoked / unknown), `classification`, `bondStatus`, `disciplinaryFlag`, `issueDate`, `expirationDate`, `sourceUrl`, `verifiedAt`.
- **You only pay when we find a match.** No charge for a clean "not found", an unsupported state, or a source error — ever.
- **Auditable by design.** Every record carries the exact `sourceUrl` it was derived from and a `verifiedAt` timestamp.
- **Two ways to query:** by license number, or by licensee / business name.
- **Two pricing tiers:** a full record, or a cheaper status-only check for recurring renewal/compliance monitoring.

### Coverage (11 states)

| State | Source (official) |
|------|--------------------|
| CO | Colorado DORA — professional & occupational licenses |
| TX | Texas TDLR — regulated licensees |
| WA | Washington L\&I — registered contractors |
| OR | Oregon CCB — construction contractors (incl. bond) |
| IL | Illinois IDFPR — professional licenses (incl. disciplinary flag) |
| NY | New York — contractor registry |
| CT | Connecticut eLicense — all credentials |
| DE | Delaware — professional regulation |
| AR | Arkansas Contractors Licensing Board |
| VA | Virginia DPOR — contractors (Class A/B/C) |
| VT | Vermont Division of Fire Safety — electrical, plumbing & trade licenses |

**Coming soon:** Florida (DBPR construction), North Carolina, West Virginia. More states are added continuously — need one that isn't here yet? Request it.

### Why not just build it yourself?

Verifying licenses across states is death by a thousand scrapers. Every board ships its data in a different shape — some as open-data APIs, some as 40 MB CSVs, some only behind a JavaScript form — and they break **silently** when a column gets renamed or a portal moves. You'd write one scraper per board, then maintain all of them forever.

This Actor collapses that into one call. We absorb the per-state format drift, normalize everything into a single schema, and run a daily health canary against every source so breakage is caught **before** it reaches your results — never returned to you as a misleading "no match." You pay only when we deliver a verified record.

### Input

```json
{
  "queries": [
    { "route": "CO", "params": { "licenseNumber": "711991" } },
    { "route": "WA", "params": { "licenseNumber": "ECOSTSC758NN" } },
    { "route": "TX", "params": { "name": "ABC Plumbing" } }
  ],
  "mode": "full"
}
```

- **`queries`** — one or more lookups. Each is `{ "route": <2-letter state>, "params": { "licenseNumber": "…" } }`, or use `"name"` to search by licensee/business name.
- **`mode`** — `full` (default, complete record) or `status` (status-only, cheaper).
- **`maxResultsPerQuery`** *(optional)* — a license number can match more than one license type; default 50.
- **`socrataAppToken`** *(optional)* — a free Socrata token raises rate limits for high-volume runs.

### Output

One flat row per result.

```json
{
  "result": "found",
  "state": "OR",
  "licenseNumber": "100215",
  "licenseeName": "BRUCE HUIE",
  "status": "active",
  "classification": "RGC (Residential General Contractor)",
  "bondStatus": "$25,000 bond with WESTERN SURETY COMPANY (expires 2028-02-06)",
  "bondAmount": 25000,
  "bondCompany": "WESTERN SURETY COMPANY",
  "bondExpirationDate": "2028-02-06",
  "disciplinaryFlag": null,
  "issueDate": "1994-06-20",
  "expirationDate": "2028-02-06",
  "asOf": null,
  "sourceUrl": "https://data.oregon.gov/resource/g77e-6bhs.json?$where=…",
  "verifiedAt": "2026-05-31T12:00:00.000Z"
}
```

`result` is `found`, `not_found`, or `source_error`. Only `found` rows are billed.

### Pricing

Pay-per-event — you pay per **successful** verification:

| Event | What you get | Price |
|------|---------------|-------|
| `license-verified` | Full normalized record | **$0.03** |
| `status-check` | Status-only result (use `mode: "status"`) | **$0.01** |

No subscription, no charge on misses. For comparison, dedicated verification APIs typically charge $0.20–$0.50+ per lookup.

### How it works

For each query the Actor routes to that state's official open-data source, fetches the matching record(s), and normalizes them into the shared schema. Where a board doesn't publish an explicit status, status is derived from the expiration date against the current public roster (i.e. "present and unexpired", not a positively-asserted flag) — this is noted per record's source.

### FAQ

**Is the data official?** It comes from official state licensing-board public records, but this Actor is unofficial and not affiliated with any board. Always confirm directly with the board before relying on a result.

**How fresh is it?** Each record shows `verifiedAt` (when we fetched it) and, where the source exposes it, `asOf` (the source's own refresh date). Some states publish daily; bulk-file states refresh on the board's own schedule.

**Can I bulk-verify?** Yes — pass many items in `queries`. A license number that maps to multiple license types returns multiple rows.

**Can I use this for employment, tenant, credit, or insurance screening?** **No.** See the disclaimer below.

***

### Legal & acceptable use

> **NOT A CONSUMER REPORT / NOT FCRA-COMPLIANT.** This Actor and its output are **not** a consumer report and the operator is **not** a consumer reporting agency under the Fair Credit Reporting Act (15 U.S.C. § 1681 et seq.). Do **not** use this data, in whole or in part, to establish eligibility for **employment, credit, insurance, housing/tenancy**, or any other FCRA-covered purpose. For those purposes, obtain a consumer report from a properly licensed consumer reporting agency.

> **Source & accuracy.** Data is sourced from official state licensing-board **public records** and reflects the source as of each record's `verifiedAt` timestamp. It may be incomplete, delayed, or out of date, and is an **unofficial** reflection — always confirm license status directly with the relevant state board before relying on it. Provided **as-is**, with no warranty of accuracy, completeness, or fitness for a particular purpose; not legal or compliance advice.

> **Unofficial / no affiliation.** This Actor is not affiliated with, sponsored by, endorsed by, or certified by any state licensing board, government agency, or data provider.

> **Your responsibility.** You are solely responsible for ensuring your use complies with the terms of use, open-data license, and applicable open-records / anti-solicitation laws of each source jurisdiction. **Prohibited uses** include FCRA-covered screening, unsolicited marketing/solicitation to licensees, building competing databases where source terms forbid it, and any unlawful, harassing, or stalking purpose.

***

*Categories: Business, Developer tools. Tags: license-verification, contractor-license, compliance, kyb, due-diligence.*

# Actor input Schema

## `queries` (type: `array`):

One or more license lookups. Each item is { "route": <2-letter state code>, "params": { "licenseNumber": "..." } } — or use "name" instead of "licenseNumber" to search by licensee/business name. Supported states: CO, TX, WA, OR, IL, NY, CT, DE, AR, VA, VT.

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

full = complete normalized record (billed as license-verified). status = status-only result, cheaper (billed as status-check).

## `maxResultsPerQuery` (type: `integer`):

Cap on records returned per query (a license number can match multiple license types).

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

A free Socrata app token raises anonymous rate limits for high-volume runs against the Socrata-backed states. Leave blank otherwise.

## `useApifyProxy` (type: `boolean`):

Route requests through Apify Proxy (only needed for proxy-required states added later; the 11 current states do not need it).

## Actor input object example

```json
{
  "queries": [
    {
      "route": "CO",
      "params": {
        "licenseNumber": "711991"
      }
    },
    {
      "route": "WA",
      "params": {
        "licenseNumber": "ECOSTSC758NN"
      }
    },
    {
      "route": "TX",
      "params": {
        "name": "ABC"
      }
    }
  ],
  "mode": "full",
  "maxResultsPerQuery": 50,
  "useApifyProxy": false
}
```

# Actor output Schema

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

One normalized record per lookup: result, state, licenseNumber, licenseeName, status, classification, expiration, bond, disciplinary flag, sourceUrl, and verifiedAt.

## `disclaimer` (type: `string`):

FCRA / not-a-consumer-report, source-accuracy, and acceptable-use notices for this run.

# 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 = {
    "queries": [
        {
            "route": "CO",
            "params": {
                "licenseNumber": "711991"
            }
        },
        {
            "route": "WA",
            "params": {
                "licenseNumber": "ECOSTSC758NN"
            }
        },
        {
            "route": "TX",
            "params": {
                "name": "ABC"
            }
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("maki-dev/us-license-verify").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 = { "queries": [
        {
            "route": "CO",
            "params": { "licenseNumber": "711991" },
        },
        {
            "route": "WA",
            "params": { "licenseNumber": "ECOSTSC758NN" },
        },
        {
            "route": "TX",
            "params": { "name": "ABC" },
        },
    ] }

# Run the Actor and wait for it to finish
run = client.actor("maki-dev/us-license-verify").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "queries": [
    {
      "route": "CO",
      "params": {
        "licenseNumber": "711991"
      }
    },
    {
      "route": "WA",
      "params": {
        "licenseNumber": "ECOSTSC758NN"
      }
    },
    {
      "route": "TX",
      "params": {
        "name": "ABC"
      }
    }
  ]
}' |
apify call maki-dev/us-license-verify --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=maki-dev/us-license-verify",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

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