# Washington Electrical Contractor Licenses Scraper (`automation-lab/washington-electrical-registry`) Actor

Search official Washington L\&I electrical contractor licenses by business name or license number. Export status, expiration, UBI and mailing location for recurring vendor verification.

- **URL**: https://apify.com/automation-lab/washington-electrical-registry.md
- **Developed by:** [Automation Lab](https://apify.com/automation-lab) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $2.40 / 1,000 item extracteds

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 Electrical Contractor Licenses Scraper

Search Washington electrical contractor licenses for recurring vendor verification. Retrieve official Washington Department of Labor & Industries (L\&I) license identity, status, expiration, UBI and mailing location in a structured dataset.

This Actor reads **L\&I's official public export on data.wa.gov**, not the interactive Verify website. The export can lag changes in Verify. For consequential decisions, independently confirm the result with L\&I.

### Who is this for?

- Procurement teams checking electrical subcontractors before onboarding.
- Property managers reconciling a vendor's license number with its business identity.
- Compliance analysts comparing repeat snapshots by license number.

This is a contractor-business lookup, not a lookup of individual electrician certificates or trainees.

### Why use this Actor?

A small input produces spreadsheet-ready license records without manually transcribing regulator pages. Output preserves the official license status instead of guessing validity from the expiration date. Electrical contractor scope is enforced on every request and record.

No source login, paid source API key, browser, or proxy is required. The Actor does not use AI to infer or enrich license information.

### Washington electrical contractor licenses: source and scope

Source: [L\&I Contractor License Data - General](https://data.wa.gov/resource/m8qx-ubtq.json), attributed to Labor & Industries, resource `m8qx-ubtq`.

Only source type `EC` (ELECTRICAL CONTRACTOR) is returned. Construction, elevator, plumbing and individual tradesperson credentials are excluded even if their name or number matches.

Records are a current export snapshot, not a license-history service. Mailing addresses may be outside Washington. No statewide record-count guarantee is made.

### Getting started

1. Open Input and enter a business name substring, such as `Electric`.
2. Alternatively clear the name and enter an exact electrical contractor license number.
3. Set `maxItems` and run the Actor.
4. Open the Dataset tab and export JSON, CSV, Excel or another format supported by Apify.
5. Review `licenseStatus`, `expirationDate` and the source link. Confirm important decisions in [L\&I Verify](https://secure.lni.wa.gov/verify/).

Example name search:

```json
{"businessName":"Electric","maxItems":10}
```

Example exact number search:

```json
{"licenseNumber":"16DLLL*882JT","maxItems":1}
```

### Inputs and matching

| Field | Behavior |
|---|---|
| `businessName` | Case-insensitive literal substring in business name; surrounding input whitespace removed. |
| `licenseNumber` | Exact full license number ignoring case and surrounding whitespace; `*` is literal. |
| `maxItems` | Maximum unique license records across the entire run; default 10, range 1–10000. |

Provide **exactly one** of `businessName` or `licenseNumber`. Neither and both are errors. Blank searches, unknown fields and invalid limits fail closed. There is no unlimited/zero limit.

Names are not tokenized: `electric` can match `electrical`. Multiple name terms are one literal substring, not ANY/ALL keyword lists. Search does not include address, phone or principal name.

Results use stable license-number ordering, deduplicate by license number, and stop on the global limit, source exhaustion or billing budget. A name can legitimately match nothing; that is a successful empty dataset, not a fabricated placeholder row.

### Extracted data

| Field | Meaning |
|---|---|
| `businessName` | Registered business identity. |
| `licenseNumber` | Official L\&I electrical contractor license identifier. |
| `licenseType` | Always ELECTRICAL CONTRACTOR. |
| `licenseStatus`, `statusCode` | Official export status and abbreviation, not calculated validity. |
| `effectiveDate`, `expirationDate` | Source dates as YYYY-MM-DD. |
| `ubi` | Unified Business Identifier preserved as text. |
| `address1`, `address2`, `city`, `state`, `zip` | Public mailing address; not a service-area claim. |
| `phone` | Public business phone as displayed. |
| `businessType`, `specialty` | Legal business structure and primary specialty. |
| `sourceUrl` | Official export URL filtered to this electrical license. |
| `verifyUrl` | Verify homepage; not a record-specific deep link. |
| `scrapedAt` | UTC retrieval time, not regulator update time. |

Missing optional source values are `null`. Identifiers and phone numbers remain strings. No bonds, insurance, violations, principal details or qualification decisions are included.

### Output example

A real exact-number lookup returned these fields (contact fields omitted here):

```json
{
  "businessName":"16D LLC",
  "licenseNumber":"16DLLL*882JT",
  "licenseType":"ELECTRICAL CONTRACTOR",
  "licenseStatus":"ACTIVE",
  "statusCode":"A",
  "expirationDate":"2028-05-15",
  "effectiveDate":"2012-04-30",
  "ubi":"603092523",
  "city":"VASHON",
  "state":"WA",
  "zip":"98070",
  "verifyUrl":"https://secure.lni.wa.gov/verify/"
}
```

The actual dataset also includes address, phone, specialty, business type, source URL and retrieval timestamp. The example is a snapshot; dates and status can change.

### How much does it cost to verify Washington electrical contractor licenses?

Pay-per-event pricing charges a one-time **$0.005 start fee** and one `item` event for each delivered unique electrical license record. No item charge is made for empty results, rejected rows or duplicate licenses. The start fee still applies to a valid empty search.

| Store spend tier | Price per license |
|---|---:|
| FREE | $0.0046 |
| BRONZE | $0.004 |
| SILVER | $0.00312 |
| GOLD | $0.0024 |
| PLATINUM | $0.0024 |
| DIAMOND | $0.0024 |

Estimated BRONZE totals: 1 record $0.009; 10 records $0.045; 100 records $0.405. FREE: 10 records approximately $0.051. These are Actor-event estimates, not guaranteed invoices; consult the current Pricing panel and Apify billing rules. Spend tiers depend on qualifying aggregate monthly Store spend, not this Actor's record volume.

The company covers its runtime dependencies; no off-platform source fee or surprise proxy charge is required. A run that fails after delivering rows may contain partial output and corresponding charges. Set a maximum charge budget appropriate to the requested limit.

### Limits, retries and troubleshooting

The Actor requests sequential pages of at most 100 source rows and holds only a page plus the deduplication set in memory. No proxy or alternate paid fallback is enabled.

Network failures, HTTP 429 and temporary 5xx responses get up to three attempts with bounded backoff and a 30-second timeout per request. Permanent HTTP failures, non-JSON responses and malformed source rows fail rather than masquerading as empty results.

If a run fails, inspect its log and partial dataset before rerunning. Broad searches may exhaust the configured timeout or charge budget before reaching the requested record limit. A source update during pagination may change coverage; use an exact license-number lookup for a critical individual check.

### Integrations and recurring verification

- Export the dataset to a spreadsheet and join on `licenseNumber` or `ubi`.
- Schedule an Apify Task with an unchanged input and compare status/expiration snapshots in your own workflow.
- Trigger a downstream integration when the run finishes; treat a failed run's output as partial.
- Send the dataset to your CRM or procurement system while retaining the official provenance link.

Scheduling, comparisons and notifications are configured separately in Apify or downstream tools. This Actor itself does not retain prior snapshots, detect changes or send alerts.

### API usage

Keep your Apify token private. Start this Actor once and retain the returned run/storage IDs.

```bash
curl -X POST 'https://api.apify.com/v2/acts/automation-lab~washington-electrical-registry/runs' \
  -H "Authorization: Bearer $APIFY_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"businessName":"Electric","maxItems":10}'
```

JavaScript with `apify-client`:

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('automation-lab/washington-electrical-registry')
  .call({ licenseNumber: '16DLLL*882JT', maxItems: 1 });
if (run.status !== 'SUCCEEDED') throw new Error(run.status);
const { items } = await client.dataset(run.defaultDatasetId).listItems({ limit: 10 });
```

Python with `apify-client`:

```python
import os
from apify_client import ApifyClient
client = ApifyClient(os.environ['APIFY_TOKEN'])
run = client.actor('automation-lab/washington-electrical-registry').call(
    run_input={'businessName': 'Electric', 'maxItems': 10})
if run['status'] != 'SUCCEEDED':
    raise RuntimeError(run['status'])
items = client.dataset(run['defaultDatasetId']).list_items(limit=10).items
```

### MCP: Claude Code, Claude Desktop, Cursor and VS Code

Claude Code setup:

```bash
claude mcp add --transport http apify \
  "https://mcp.apify.com?tools=automation-lab/washington-electrical-registry"
```

Claude Desktop / Cursor / VS Code MCP configuration (adapt the enclosing key to your client):

```json
{"mcpServers":{"apify":{"url":"https://mcp.apify.com?tools=automation-lab/washington-electrical-registry"}}}
```

Authenticate through your client's supported Apify method; do not paste tokens into prompts. Use scoped `tools/list` to discover actual names and schemas. Actor selection also adds run/storage/abort tools; it does not expose exactly one tool. Read-only discovery does not authorize execution.

#### Example prompts

- “Run Washington Electrical Contractor Licenses Scraper for exact license 16DLLL\*882JT, maximum one record. Show the official status, expiration and source link.”
- “Search business names containing Electric, return at most ten electrical contractor licenses, and summarize their official statuses.”

Start once, keep the run ID, and follow that same run. `call-actor` waits at most `waitSecs` (0–45 seconds, default 30); a tool wait may end before the run finishes. Configure the client's request timeout to exceed the chosen `waitSecs` plus a transport margin while staying within the total deadline. Poll that same run with `get-actor-run`, bounded backoff (2, 4, 8 seconds, capped at 10) and a total deadline such as 120 seconds; on a client timeout recover the known run, never blindly start another. If the run ID is unknown, report uncertainty instead of restarting. At the deadline report pending with the run ID; failed runs are not complete successes and any available rows are partial.

After success, use `get-dataset-items` with the returned `defaultDatasetId` and explicit arguments matching its discovered schema: `limit: 20`, `offset: 0`, and `fields` selecting `businessName`, `licenseNumber`, `licenseStatus`, `expirationDate`, and `sourceUrl`. Prefer untransformed JSON and advance `offset` using source-row pagination metadata. Read bounded dataset pages: no more than 100 rows or 64 KiB of serialized UTF-8 content admitted to model context across all pages, whichever comes first. Enforce byte limits host-side; if your client cannot intercept oversized responses, do not promise a hard byte guarantee. Track source offsets, report truncation/continuation, and keep full exports outside model context. Tool metadata and dataset IDs are not result rows. Client caching does not guarantee universal token savings.

### Legality, responsible use, privacy and retention

This independent Actor is not affiliated with or endorsed by Washington L\&I. It uses Apify's standard terms. Use public records responsibly and comply with applicable law and source conditions. Records are evidence for review, not legal advice or a guarantee that a contractor may perform a specific job.

Inputs contain a business query; results contain public business identity/contact data that can identify sole proprietors. Principal/owner fields are deliberately omitted. Logs contain operational counts/errors, not successful record bodies. No AI provider receives runtime input or output.

Run inputs, results and logs are stored in your Apify account under its retention settings; delete run storage through Apify when no longer needed. The Actor creates no cross-run business cache or persistent session.

Failed operations send sanitized diagnostic input, exceptions and actor/build/run IDs to our private GlitchTip service for repair. Secret fields and URL queries are removed; diagnostic reports are retained for 30 days. Do not put unnecessary confidential information in search fields.

### FAQ and support

**Why did my license search return no record?** Confirm the exact number and punctuation. Construction registrations and individual electrician credentials are outside electrical contractor scope.

**Does ACTIVE guarantee suitability?** No. It is the source-published status. Independently check scope, dates and regulator records; this Actor does not evaluate insurance or eligibility.

**Is this real-time Verify data?** No. It is the official public export. `scrapedAt` means retrieval time and does not certify when L\&I last refreshed a record.

**Can I search multiple businesses?** Each run accepts one name or license number. Create separate Tasks or orchestrate calls if needed; each run has its own start fee and dataset.

For incorrect output or a source change, open an issue on this Actor with the input, run link and expected behavior, avoiding confidential details.

### Related Actors

For another jurisdiction's electrical license records, see [North Carolina Electrical License Search](https://apify.com/automation-lab/north-carolina-electrical-license-search). It uses NCBEEC, not Washington L\&I. This Actor remains a standalone Washington electrical-contractor verification workflow.

# Changelog

This Actor's version history is a separate document: https://apify.com/automation-lab/washington-electrical-registry/changelog.md

# Actor input Schema

## `businessName` (type: `string`):

Case-insensitive literal substring in the business name, with surrounding input whitespace removed. Provide this OR licenseNumber, not both. No individual electrician credentials are searched.

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

Exact full license-number match ignoring case and surrounding input whitespace. Symbols such as \* are literal. Provide this OR businessName, not both.

## `maxItems` (type: `integer`):

Global maximum unique electrical licenses per run, after filtering and deduplication. Default 10; range 1–10000; zero/unlimited is unsupported. Stable license-number ordering. Upstream exhaustion or charge budget can stop earlier.

## Actor input object example

```json
{
  "businessName": "Electric",
  "maxItems": 10
}
```

# Actor output Schema

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

Current-run electrical license dataset with status, expiration, UBI and source links.

# 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 = {
    "businessName": "Electric"
};

// Run the Actor and wait for it to finish
const run = await client.actor("automation-lab/washington-electrical-registry").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 = { "businessName": "Electric" }

# Run the Actor and wait for it to finish
run = client.actor("automation-lab/washington-electrical-registry").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 '{
  "businessName": "Electric"
}' |
apify call automation-lab/washington-electrical-registry --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,automation-lab/washington-electrical-registry"
        }
    }
}
```

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/bs6mMdHbc7klUysdN/builds/U4ulXnBX8x0b5z2Tb/openapi.json
