# Building Permit Leads with Contractor License Status (`vanakenj45/permit-leads-license-status`) Actor

New building permits, each checked against state contractor license records: license number, status, expiry and insurance. Tacoma, WA today; more Pacific Northwest cities as permission is granted. Filter by trade, project value and date.

- **URL**: https://apify.com/vanakenj45/permit-leads-license-status.md
- **Developed by:** [Vanaken Digital](https://apify.com/vanakenj45) (community)
- **Categories:** Lead generation, Business, Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $25.00 / 1,000 permit delivereds

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/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

## Building Permit Leads with Contractor License Status

**Know who's building before you call.** Every new building permit comes with the contractor's
**license status from official state records**: license number, active or not, license expiry and
insurance expiry, plus a normalized trade so you can go straight to the jobs that matter to you.

**Coverage today: Tacoma, WA** (City of Tacoma permit data, refreshed daily by the City).
More Pacific Northwest cities will be added as permission is granted.

### Who it is for

- **Subcontractors and installers** (roofing, solar, HVAC, electrical, plumbing, fire protection) looking for jobs in progress.
- **Material suppliers and equipment rental** who want to reach active, properly licensed builders.
- **Insurers, bonding agents and lenders** watching for projects by contractors whose license or insurance is lapsing.
- **AI agents and automations** that need a daily permit-lead feed (it works as an MCP tool).

### What you get for each permit

| Field | What it is |
|---|---|
| `permit_id`, `permit_type`, `permit_subtype` | As published by the City |
| `trade` | Our normalized trade: `new_construction`, `addition_alteration`, `roofing`, `solar`, `hvac_mechanical`, `electrical`, `plumbing`, `fire_protection`, `demolition`, `sign`, `site`, `right_of_way`, `utility`, `other`. `trade_confidence` is `high` when it comes from the City's permit category, `low` when it is read from the description. |
| `status`, `status_detail` | `in_review`, `issued`, `finaled`, `expired`, `cancelled`, plus the City's own wording |
| `applied_date`, `issued_date` | |
| `valuation_usd`, `housing_units` | The declared project value and new units |
| `site_address`, `zip`, `lat`, `lon`, `parcel_number` | Where the work is |
| `description` | The work description as filed |
| `source_url` | The permit's official record |
| `contractor` | The matched contractor license, or `null` (see below) |
| `attribution`, `terms_url` | The City's required disclaimer and its source page |

#### Contractor license status

- **What's included.** For a business license: `license_number`, `business_name`, `license_type`,
  `specialties`, `license_status` (for example ACTIVE, EXPIRED, SUSPENDED), `license_expires` and
  `insurance_expires`, with `match_method` and `match_confidence`.
- **Individual license holders.** When the license belongs to an individual (a sole
  proprietor), only the license number and the status, expiry and insurance dates are shown. No
  name.
- **How matching works.** Tacoma's permit data doesn't include a license number, so permits are
  matched by **exact business name** (ignoring punctuation and LLC/Inc-style suffixes) against
  Washington L\&I contractor licenses:
  - `medium` confidence means exactly one active business license has that name.
  - `low` means a weaker match (an individual, an inactive license, or several candidates of
    which only one is active). Low matches are hidden unless you set `min_match_confidence` to `low`.
  - If two different licenses could fit, we show no match rather than guess.
  - When a city publishes license numbers on its permits, those matches are exact (`high`).
- Turn on `require_contractor_match` to receive only permits with a matched license.

### Input

| Field | Default | What it does |
|---|---|---|
| `cities` | all available | Today: `tacoma` |
| `trades` | all | e.g. `["roofing", "solar"]` |
| `since`, `until` | the last 7 days | Dates (YYYY-MM-DD), up to a year back |
| `date_field` | `issued_date` | Or `applied_date`, for permits still in review |
| `statuses` | `["issued"]` | Any of `in_review`, `issued`, `finaled`, `expired`, `cancelled` |
| `min_valuation_usd`, `max_valuation_usd` | none | Project value range |
| `require_contractor_match` | `false` | Only permits with a matched license |
| `min_match_confidence` | `medium` | `high`, `medium` or `low` |
| `max_permits` | 100 | At most 2,000 per run |

Example: roofing and solar jobs worth $20,000 or more, issued since October 1, 2026.

```json
{ "trades": ["roofing", "solar"], "min_valuation_usd": 20000, "since": "2026-10-01" }
```

For a daily feed, schedule the Actor every morning with `since` set to yesterday.

### Output example

A real item from a run on 2026-10-22 (the `attribution` text is shortened here):

```json
{
  "permit_id": "FIREC26-0209",
  "permit_type": "Building",
  "permit_subtype": "Commercial",
  "trade": "fire_protection",
  "trade_confidence": "high",
  "status": "issued",
  "status_detail": "Permit Issued",
  "applied_date": "2026-09-23",
  "issued_date": "2026-10-16",
  "valuation_usd": 60768,
  "housing_units": 0,
  "site_address": "602 N SPRAGUE AVE, TACOMA,WA 98403",
  "zip": "98403",
  "lat": 47.2563,
  "lon": -122.4645,
  "parcel_number": "0320061056",
  "description": "Modification to existing fire alarm system for tenant improvements in Commercial Bldg. at Hilltop Heritage. Installation of (49) new devices and (1) new panel.",
  "source_url": "https://aca-prod.accela.com/TACOMA/urlrouting.ashx?type=1000&Module=Permits&capID1=DUB26&capID2=00000&capID3=00KAP&agencyCode=TACOMA",
  "source": "tacoma",
  "jurisdiction": "Tacoma, WA",
  "state": "WA",
  "contractor": {
    "license_state": "WA",
    "license_number": "JOHNSC*272OS",
    "entity_type": "business",
    "license_status": "ACTIVE",
    "license_expires": "2028-10-28",
    "insurance_expires": "2027-10-01",
    "bond_expires": null,
    "match_method": "name",
    "match_confidence": "medium",
    "business_name": "JOHNSON CONTROLS INC",
    "license_type": "CONSTRUCTION CONTRACTOR",
    "specialties": [
      "GENERAL"
    ]
  },
  "attribution": "The information and data made available here has been modified for use from its original source,… (the full required disclaimer)",
  "terms_url": "https://data.tacoma.gov/pages/disclaimer"
}
```

### Pricing

**$0.025 per permit delivered** (pay per event). You pay only for permits written to your dataset;
permits filtered out, or skipped for lack of a contractor match when you require one, are free.
Apify platform usage is included, and Apify adds its standard run-start fee of $0.00005. Use
`max_permits` and Apify's per-run spending limit to cap a run; the Actor stops cleanly at your limit.

### Limitations (please read)

- **Coverage.** Tacoma, WA only for now. More Pacific Northwest cities will be added only as their
  data owners grant permission.
- **Matching.** Matches come from business names, so a contractor filed under a different name,
  or a homeowner pulling their own permit, shows `contractor: null`. Confidence levels tell you how
  sure the match is; check the license on the state's site before relying on it.
- **License data.** Washington records are business-level only. Bond status isn't included for
  Washington, because the state's bond dataset doesn't state a reuse license. Insurance comes from
  L\&I's published insurance data.
- **No personal names.** Permit applicant, property owner, and license principal names are never
  delivered, and individuals' license records are shown without a name. Phone numbers and mailing
  addresses from license records aren't included.
- **City data can change or stop.** The City publishes and updates the permit data and may
  withdraw it. If a source withdraws permission, it's removed from this Actor.
- **No code-enforcement data** from any source.

### Required disclaimer (City of Tacoma)

> The information and data made available here has been modified for use from its original source, which is the City of Tacoma. The City of Tacoma makes no claims as to the completeness, accuracy, or timeliness of any information and data contained in this application; makes no representation of any kind, including, but not limited to, warranty of the accuracy or fitness for a particular use; nor are any such warranties to be implied or inferred with respect to the information or data furnished herein. The information and data is subject to change without notice. It is understood that the information and data contained in the web site is being used at user’s own risk.

Source terms: https://data.tacoma.gov/pages/disclaimer. Every dataset item also carries this text in `attribution`.

### Use it from an AI agent (MCP)

This Actor runs with Apify's default limited permissions, so it is available through Apify's MCP server:

```
https://mcp.apify.com?tools=vanakenj45/permit-leads-license-status
```

An agent can call it with input like `{"trades": ["roofing"], "require_contractor_match": true}` and read the results with `get-dataset-items`.

### Data sources and licenses

- **Permits:** City of Tacoma, Accela permit data on the City's open data portal (ArcGIS item `a12d6fbf58e4434b8ff5070c09646f19`), used under the City's open data terms with the disclaimer above.
- **Washington contractor licenses:** Washington State Department of Labor & Industries, "L\&I Contractor License Data - General" (data.wa.gov `m8qx-ubtq`, Open Data Commons Public Domain Dedication and License) and "L\&I Contractor License Data - Insurance" (`ciwg-agsx`, public domain).

# Actor input Schema

## `cities` (type: `array`):

Cities to include. Default: every city available.

## `trades` (type: `array`):

Only these trades (from the permit category, or the description when there is none). Default: all.

## `since` (type: `string`):

YYYY-MM-DD, inclusive. Default: 7 days ago.

## `until` (type: `string`):

YYYY-MM-DD, exclusive. Default: today.

## `date_field` (type: `string`):

Which date the From/To filter applies to. Default: issue date.

## `statuses` (type: `array`):

Default: issued.

## `min_valuation_usd` (type: `integer`):

Only permits with a declared value at least this.

## `max_valuation_usd` (type: `integer`):

Only permits with a declared value at most this.

## `require_contractor_match` (type: `boolean`):

Deliver (and charge for) only permits matched to a contractor license.

## `min_match_confidence` (type: `string`):

Lowest match confidence to show a contractor. Default: medium.

## `max_permits` (type: `integer`):

Most permits to deliver (and be charged for) in this run.

## Actor input object example

```json
{
  "date_field": "issued_date",
  "statuses": [
    "issued"
  ],
  "require_contractor_match": false,
  "min_match_confidence": "medium",
  "max_permits": 50
}
```

# Actor output Schema

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

One item per permit: normalized trade, status, dates, value, site address, description, official link, the matched contractor's license status (or null), and the source's required disclaimer.

# 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 = {
    "max_permits": 50
};

// Run the Actor and wait for it to finish
const run = await client.actor("vanakenj45/permit-leads-license-status").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 = { "max_permits": 50 }

# Run the Actor and wait for it to finish
run = client.actor("vanakenj45/permit-leads-license-status").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 '{
  "max_permits": 50
}' |
apify call vanakenj45/permit-leads-license-status --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,vanakenj45/permit-leads-license-status"
        }
    }
}
```

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/PoMsRd96KglnofY7E/builds/E0LJia5YL4NTGb0CU/openapi.json
