# WA Dealer & Transporter Licenses - Bonds & Surety (`j0401/wa-dol-transport`) Actor

Washington vehicle, vessel & transporter licenses (public DOL data, 38.5k locations, monthly): dealers, transporters, limousine, tow-truck and scrap operators with the surety-bond block a plain dealer list omits - bond status, amount, company, dates. Filter by type/name/number or pull active bonds.

- **URL**: https://apify.com/j0401/wa-dol-transport.md
- **Developed by:** [Wenhao Yang](https://apify.com/j0401) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.06 / 1,000 wa dealer 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

## WA Dealer & Transporter Licenses - With the Bond Block

The Washington Department of Licensing publishes its **dealer and transporter license file** - every licensed vehicle, vessel, transporter, tow and scrap-metal business location in the state - as open data. This actor turns that file into a **charged-per-record lookup with the surety-bond detail a plain dealer list leaves out**.

**Built for:** lenders and floor-plan financiers verifying a dealer's bond, wholesale and auction platforms onboarding dealers, dealership M\&A and territory research, and compliance checks on tow, transporter and scrap operators.

### What it covers

**38,514 licensed locations**, refreshed ~monthly, across 24 license types:

| License type | Locations |
|---|---|
| Motor Vehicle Dealer | 8,457 |
| Vehicle Transporter | 8,280 |
| For Hire | 3,740 |
| Limousine Carrier | 1,980 |
| Vessel Dealer | 1,925 |
| MVD Subagency / Misc Dealer / Wholesaler / Tow Truck / Manufacturer / Mfd-Home / Wrecker / Snowmobile / Salvage / Scrap Metal | the long tail |

Status is the state's own four values - **Active 8,271** / Expired 17,255 / Terminated 12,758 / Held 230 - and licensees cluster where the market is: Seattle (3,794 licensee addresses), Spokane (1,920), Tacoma (1,769), Kent (1,476), Everett (1,071).

Each record carries the licensee and the site:

- **license number**, **type** and **status**
- **licensee name and address** plus the **site / DBA name and address** (they differ - the licensed party and the lot often have separate addresses)
- **phone**
- first-issue and expiration dates

### The bond block - the depth the competition omits

Every row carries the **surety bond** behind it, which a shallow "dealer list" scraper does not:

- **bond status** - Active 7,183 / Bond Not Required 16,986 / No Bond information on record 14,345
- **bond amount** (from $0 to $30,000,000 across the active bonds)
- the **bonding company**, its **phone** and **address**
- the **bond number** and its **effective / cease dates**

Bonds are held by the **dealer family**, not the transporter lanes: Active bonds concentrate in Motor Vehicle Dealer (3,331), Vessel Dealer (650), MVD Subagency (615) and Registered Tow Truck Operator (593) - exactly the operators a financier or platform needs to vet.

### One license number, many sites

The row grain in this file is a **licensed location**, not a licensee - and that trips up naive reads two ways. A single dealer number can sit on **many sites**: Home Depot holds 43 store locations under the number `06972`. And old numbers get **reused across unrelated businesses**: `00101` carries seven different companies at seven addresses. So `license_number` is *not* a unique key - 38,514 rows carry only 22,478 distinct numbers. This actor handles both: match a number to get every site it has ever covered, or match a business name to get every site it is licensed at.

The expiration field is also a trap: **27,331 rows carry the sentinel `9999-12-31`** meaning "no expiration on file" - read literally, thousands of dead licenses look valid forever. Status is the only honest active read, and the sentinel is cleared from the output so it never surfaces as a real date.

### Typical questions

- "Is this dealer's **bond** active, for how much, and with which surety?"
- "Every **active Motor Vehicle Dealer** in King County's cities."
- "**Vessel dealers** with a current bond - the marina-facing list."
- "Which sites does license number `06972` cover?"
- "**Every licensed location** for a business name."
- "Aggregate by **license type**, **city**, **bond status**, or **first-issue year**."

### Inputs

| Input | What it does |
|---|---|
| `mode` | `rows` (default) / `active` (status Active) / `bond` (active surety bond) / `aggregate` |
| `licenseType` | a lane: `dealer` / `vessel` / `transporter` / `limousine` / `tow` / `wholesaler` / `manufacturer` / ... |
| `status` | exact: Active / Expired / Terminated / Held |
| `licenseNumber` | exact number (returns every site it covers) |
| `name` / `city` | business/site name and city (fuzzy) |
| `bondingCompany` | surety company (fuzzy) |
| `issuedFrom/To`, `expiresFrom/To` | issue and expiration windows |
| `groupBy` | aggregate over licenseType / status / city / bondStatus / firstIssueYear |
| `maxResults` | cap records (default 50) |

**Default run = the 50 most recently issued licenses** (mode defaults to `rows`) - fast for the daily auto-test.

### Example inputs

**Every site one number covers** - `licenseNumber` is exact, and a reused number returns each business behind it.

```json
{ "licenseNumber": "06972" }
```

**Vessel dealers with a current bond** - `mode=bond` keeps the rows with an active surety bond on file.

```json
{ "mode": "bond", "licenseType": "vessel", "maxResults": 10 }
```

**A business name across sites** - `name` matches the licensee or the site/DBA name.

```json
{ "name": "HOME DEPOT", "maxResults": 10 }
```

**The statewide license-type mix** - `mode=aggregate` returns one count row per group.

```json
{ "mode": "aggregate", "groupBy": "licenseType" }
```

### Low cost

**From $0.0001 per record, down to $0.00006 at Gold** - billed only for the rows you use, at the low end of the store. A bond check on one dealer, or the whole active-dealer list with bonds, costs pennies.

The file reads like a plain register and is dirty enough to punish a quick read. **The 9,999 sentinel** makes thousands of lapsed licenses look perpetually valid. **The number is not a key** - reused across businesses and split across sites, so a "look up this dealer" either returns one row or forty-three depending on which number you meant. **Licensee and site addresses diverge**, and **the bond columns are three-quarters empty** ("Not Required" / "No Bond information on record") with the real bonding detail living only on the dealer family. Normalizing all of that into a register where a `licenseType` / `status` / `bondingCompany` query returns exactly the locations you mean is the actual product. Every pull is integrity-checked against the file's known shape, so a degraded source fails loudly instead of returning bad rows.

### Example output

**One licensed location** - `licenseNumber="18062"` returns records like this one (with the bond block behind it):

```json
{
  "platform": "wa-dol-transport",
  "source": "washington-dol-dealer-licenses",
  "mode": "rows",
  "groupKey": "",
  "groupCount": "",
  "groupBy": "",
  "licenseNumber": "18062",
  "licenseType": "Scrap Metal Processor",
  "status": "Active",
  "expirationDate": "2027-07-31",
  "firstIssueDate": "2026-07-31",
  "siteName": "DICKSON IRON & METALS, INC.",
  "siteStreet": "907 N DYER RD",
  "siteCity": "SPOKANE VALLEY",
  "siteState": "WA",
  "siteZip": "99212",
  "businessName": "DICKSON IRON & METALS, INC.",
  "businessStreet": "907 N DYER RD",
  "businessCity": "SPOKANE VALLEY",
  "businessState": "WA",
  "businessZip": "99212",
  "phone": "(509) 535-6146",
  "bondStatus": "Active",
  "bondAmount": "0",
  "bondingCompany": "COCHRANE & CO",
  "bondNumber": "100262286",
  "bondEffectiveDate": "",
  "bondCeaseDate": "",
  "bondCompanyPhone": "(509) 838-0655",
  "bondCompanyAddress": "PO BOX 19150 SPOKANE WA 99219-9150"
}
```

**`mode=aggregate`, `groupBy=licenseType`** - one row per license lane (`groupKey` + `groupCount`):

```
Motor Vehicle Dealer   8,457
Vehicle Transporter    8,280
For Hire               3,740
Limousine Carrier      1,980
```

### Source

- [Washington Open Data: Business and professional licensing](https://data.wa.gov/dataset/Business-and-Professional-Licensing/ucdg-xgbj) - the WA Dept. of Licensing's published register. Reflects the department's record as of each refresh; not a verification of any licensee's current standing.

# Actor input Schema

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

rows = licenses matching your filters, most recent first (the default). active = the live slice (status Active). bond = licenses with an active surety bond on file. aggregate = one count row per group (see groupBy).

## `licenseType` (type: `string`):

A license lane, e.g. 'dealer', 'vessel', 'transporter', 'limousine', 'tow', 'wholesaler', 'manufacturer', 'snowmobile', 'scrap'. Blank = every type.

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

Exact status: Active, Expired, Terminated, Held. Blank = any.

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

Exact license number. Note a number can sit on several locations (and old numbers get reused) - you get every site it covers.

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

Fuzzy match on the licensee name or the site/DBA name, e.g. 'HOME DEPOT' or 'YACHT SALES'.

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

Matches the licensee city or the site city (fuzzy).

## `bondingCompany` (type: `string`):

Surety company name (fuzzy), e.g. 'TRAVELERS' or 'WESTERN SURETY'.

## `issuedFrom` (type: `string`):

Only licenses first issued on/after this date (YYYY-MM-DD).

## `issuedTo` (type: `string`):

Only licenses first issued before this date. ISO date, YYYY-MM-DD (e.g. 2026-01-01).

## `expiresFrom` (type: `string`):

Only licenses expiring on/after this date (records with no expiration on file are excluded). ISO date, YYYY-MM-DD (e.g. 2026-01-01).

## `expiresTo` (type: `string`):

Only licenses expiring before this date. ISO date, YYYY-MM-DD (e.g. 2026-01-01).

## `groupBy` (type: `string`):

Which dimension to aggregate over. licenseType -> the dealer mix; city -> licensed-location count by the LICENSEE city (the city filter also matches the site city, so the two can differ slightly); bondStatus -> how many carry a surety bond; firstIssueYear -> the licensing pipeline over time.

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

Cap the number of records pushed (0 = up to ~10k per run; each record is metered individually, so there is no per-run charge cap). An aggregate has at most a few hundred groups.

## Actor input object example

```json
{
  "mode": "rows",
  "licenseType": "",
  "status": "",
  "licenseNumber": "",
  "name": "",
  "city": "",
  "bondingCompany": "",
  "issuedFrom": "",
  "issuedTo": "",
  "expiresFrom": "",
  "expiresTo": "",
  "groupBy": "licenseType",
  "maxResults": 50
}
```

# Actor output Schema

## `recordsUrl` (type: `string`):

Washington dealer & transporter license records or aggregates - as JSON

## `datasetUrl` (type: `string`):

No description

## `runUrl` (type: `string`):

No description

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("j0401/wa-dol-transport").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("j0401/wa-dol-transport").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 '{}' |
apify call j0401/wa-dol-transport --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,j0401/wa-dol-transport"
        }
    }
}
```

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/nxqcv3QL5pyAVM1NO/builds/TzWstVqlctHceqfZy/openapi.json
