# Washington Contractor Directory & Leads Scraper (L\&I) (`muhammadafzal/washington-contractor-directory-leads-scraper`) Actor

Search Washington L\&I contractor records by name, license or UBI. Export public registration status, business contacts, location, bond and insurance details.

- **URL**: https://apify.com/muhammadafzal/washington-contractor-directory-leads-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $8.00 / 1,000 washington contractor 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

## Washington Contractor Directory & Leads Scraper (L\&I)

Search Washington State Department of Labor & Industries (L\&I) public contractor and tradesperson records by business or owner name, license number, or UBI. The Actor returns official registration details, business contact fields, location, owners, bond and insurance history, and source links for contractor prospecting and license research.

### What it returns

| Field group | Output fields |
| --- | --- |
| Identity | `businessName`, `licenseNumber`, `ubi`, `contractorType`, `licenseType` |
| Registration | `registrationStatus`, `isActiveRegistration`, `effectiveDate`, `expirationDate`, `suspendDate` |
| Contact and location | `businessPhone`, `address1`, `address2`, `city`, `state`, `zipCode`, `county` |
| Qualification | `workersCompAccountCurrent`, `hasSafetyViolation`, `hasLicenseViolation` |
| Public business records | `businessOwners`, `bondHistory`, `insuranceHistory` |
| Verification | `detailFetched`, `sourceUrl`, `sourceSearchUrl`, `scrapedAt` |

Owner names and roles are included when the L\&I record lists them. Birth dates, workers’ compensation account numbers, bond account numbers, and insurance policy/account identifiers are excluded.

### Search behavior and limits

L\&I's public lookup requires a search term. Choose a partial name, contractor/tradesperson license, or exact nine-digit UBI; the portal does not provide an empty-query statewide export. The default query is `Cascade`, the default type is `Construction Contractor`, and the default is to return only active registrations. Type, city, ZIP, active-registration, and workers’ compensation filters are applied locally to public search/detail results because L\&I's optional search facets currently return an error. A five-digit ZIP input also matches the first five digits of ZIP+4 source values.

The Actor reads up to `maxPages` (1–25, 20 search rows per page), requests candidate public detail records sequentially, and stops at `maxResults` (1–100). Requests are paced and retried a small number of times. If detail data fails for one record, search-list fields may still be delivered with `detailFetched: false` only when they satisfy the selected local filters; filters that need a detail field exclude rows whose value cannot be verified. `OUTPUT` reports source matches, rows scanned, pages fetched, and whether the page cap stopped the scan. A blocked or unavailable source is reported in `OUTPUT` and terminal status; the Actor does not invent fallback records.

Washington L\&I says its contractor/tradesperson lookup is updated Monday through Friday by 5 a.m. Pacific Time, excluding state holidays. The agency also cautions that records may be inaccurate or not current. Check the linked L\&I detail page before making licensing, hiring, or credit decisions.

### Input examples

Search a contractor name:

```json
{
  "searchType": "NAME",
  "searchTerm": "Cascade",
  "contractorTypes": ["Construction Contractor"],
  "activeRegistrationOnly": true,
  "maxResults": 10
}
```

Find a contractor by license number:

```json
{
  "searchType": "LICENSE",
  "searchTerm": "CASCAAL962RW",
  "contractorTypes": ["Construction Contractor"],
  "maxResults": 1
}
```

Search an electrical contractor type in Seattle:

```json
{
  "searchType": "NAME",
  "searchTerm": "Electric",
  "contractorTypes": ["Electrical Contractor"],
  "city": "SEATTLE",
  "activeRegistrationOnly": true,
  "maxResults": 25
}
```

The same input object can be sent through the Apify API or an Actor Task. `OUTPUT` contains counts, warnings, free-plan cap details, and event totals; the default dataset contains contractor records.

### Dataset example

Fictional example showing the field shape:

```json
{
  "recordId": "ubi:123456789|license:EXAMPL123AB",
  "businessName": "EXAMPLE CONSTRUCTION LLC",
  "licenseNumber": "EXAMPL123AB",
  "ubi": "123456789",
  "contractorType": "Construction Contractor",
  "licenseType": "General Contractor",
  "registrationStatus": "ACTIVE",
  "isActiveRegistration": true,
  "businessPhone": "2065550100",
  "city": "SEATTLE",
  "state": "WA",
  "zipCode": "98101",
  "county": "KING",
  "businessOwners": [{ "name": "Jordan Example", "role": "MEMBER", "endDate": null }],
  "bondHistory": [],
  "insuranceHistory": [],
  "workersCompAccountCurrent": true,
  "hasSafetyViolation": false,
  "hasLicenseViolation": false,
  "detailFetched": true,
  "sourceUrl": "https://secure.lni.wa.gov/verify/Detail.aspx?UBI=123456789&LIC=EXAMPL123AB&SAW=",
  "sourceSearchUrl": "https://secure.lni.wa.gov/verify/",
  "scrapedAt": "2026-09-23T12:00:00.000Z"
}
```

### Pay-per-event pricing

One `apify-actor-start` event is charged automatically when a run starts (one start event per GB of memory, with a minimum of one). Each validated record written to the default dataset is one `apify-default-dataset-item` event; there is no second custom charge for the same record. The live private Actor uses pay-per-event pricing with platform usage included:

| Apify subscription tier | Record discount | Contractor record | Start event |
| --- | ---: | ---: | ---: |
| FREE | 0% | $0.01000 | $0.005 |
| BRONZE | 2.5% | $0.00975 | $0.005 |
| SILVER | 5% | $0.00950 | $0.005 |
| GOLD | 20% | $0.00800 | $0.005 |

The event prices per 1,000 contractor records are $10.00 (FREE), $9.75 (BRONZE), $9.50 (SILVER), and $8.00 (GOLD). PLATINUM and DIAMOND use the GOLD record rate in the current live schedule. Example event totals below assume the default 256 MB memory allocation, which incurs the minimum one start event:

| Tier | 1 record | 10 records |
| --- | ---: | ---: |
| FREE | $0.01500 | $0.10500 |
| BRONZE | $0.01475 | $0.10250 |
| SILVER | $0.01450 | $0.10000 |
| GOLD, PLATINUM, or DIAMOND | $0.01300 | $0.08500 |

The minimum `maxTotalChargeUsd` is $0.015. This limit applies to event charges, not platform usage. The Actor stops before writing another record when there is not enough event budget for it.

Verified Apify free-plan users receive at most **5 delivered records per run**. Paid and agentic runs can use the requested maximum of 100. A missing plan value during local development is treated as unknown, not as a free run.

### Source and compliance

- [L\&I Verify a Contractor, Tradesperson or Business](https://secure.lni.wa.gov/verify/)
- [L\&I lookup description and data disclaimer](https://lni.wa.gov/licensing-permits/contractors/hiring-a-contractor/verify-contractor-tradesperson-business)

Use this Actor to search public L\&I business records. It does not determine whether a contractor is suitable for a particular project, guarantee current registration, or replace direct verification with Washington L\&I. Follow the source's terms and applicable rules when contacting businesses or using public owner information.

# Actor input Schema

## `searchType` (type: `string`):

Choose how the official L\&I portal interprets searchTerm. Use NAME for a partial business or owner name, LICENSE for a contractor registration or tradesperson license, or UBI for an exact nine-digit Washington UBI.

## `searchTerm` (type: `string`):

Use this for a 2–32 character business/owner name or license search, such as `Cascade` or `CASCAAL962RW`. For UBI search, provide exactly 9 digits, such as `602156869`. This follows the L\&I portal's search behavior; it is not a free-text address or trade filter.

## `contractorTypes` (type: `array`):

Choose one or more official L\&I contractor or tradesperson types. The Actor applies this filter locally to the public search results because the portal's optional type facet currently returns an error.

## `city` (type: `string`):

Use this for an exact city from the L\&I search results, such as `SEATTLE` or `TACOMA`. Leave blank to include all cities returned for the search. This is the source mailing city, not a service-area claim.

## `zipCode` (type: `string`):

Use a five-digit ZIP code, such as `98101`. If L\&I returns ZIP+4, the Actor matches its first five digits. Leave blank to include all ZIP codes returned for the search.

## `activeRegistrationOnly` (type: `boolean`):

When true, returns only details whose L\&I registration status is ACTIVE. This is checked locally against each public detail record.

## `activeWorkersCompOnly` (type: `boolean`):

When true, returns only businesses whose public L\&I detail record reports a current workers’ compensation account. Records without a verifiable current account are excluded.

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

Use this to cap returned contractor records and event charges. Default 10; minimum 1 and maximum 100. Verified free-plan runs are capped at 5 delivered records even when a higher value is requested; paid and agentic runs are not capped by this rule.

## `maxPages` (type: `integer`):

Caps the number of public L\&I search pages scanned. Each page contains up to 20 search rows. Increase this when local filters return fewer records than requested; the run summary reports when this cap stops the scan.

## Actor input object example

```json
{
  "searchType": "NAME",
  "searchTerm": "Cascade",
  "contractorTypes": [
    "Construction Contractor"
  ],
  "activeRegistrationOnly": true,
  "activeWorkersCompOnly": false,
  "maxResults": 10,
  "maxPages": 10
}
```

# Actor output Schema

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

Schema-validated L\&I business and license records from the default dataset.

## `dataset` (type: `string`):

Default dataset in Apify Console.

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

Outcome, limits, counts, event totals, and warnings 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 = {
    "searchType": "NAME",
    "searchTerm": "Cascade",
    "contractorTypes": [
        "Construction Contractor"
    ],
    "activeRegistrationOnly": true,
    "activeWorkersCompOnly": false,
    "maxResults": 10,
    "maxPages": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/washington-contractor-directory-leads-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 = {
    "searchType": "NAME",
    "searchTerm": "Cascade",
    "contractorTypes": ["Construction Contractor"],
    "activeRegistrationOnly": True,
    "activeWorkersCompOnly": False,
    "maxResults": 10,
    "maxPages": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/washington-contractor-directory-leads-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 '{
  "searchType": "NAME",
  "searchTerm": "Cascade",
  "contractorTypes": [
    "Construction Contractor"
  ],
  "activeRegistrationOnly": true,
  "activeWorkersCompOnly": false,
  "maxResults": 10,
  "maxPages": 10
}' |
apify call muhammadafzal/washington-contractor-directory-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/washington-contractor-directory-leads-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/5k44fHXP1y7XBmzEI/builds/RwEn5UAMbyU3ZMB0x/openapi.json
