# North Carolina Plumbing & HVAC Contractor Leads Scraper (`memo23/north-carolina-plumbing-hvac-contractor-leads-scraper`) Actor

Scrape license-verified plumbing, HVAC & fire-sprinkler contractor leads from North Carolina's state licensing board — company, license number, active classifications, expiry, address, phone, classification holders, and technician-employer links. Full-state sweep or targeted search.

- **URL**: https://apify.com/memo23/north-carolina-plumbing-hvac-contractor-leads-scraper.md
- **Developed by:** [Muhamed Didovic](https://apify.com/memo23) (community)
- **Categories:** Lead generation, Agents, MCP servers
- **Stats:** 1 total users, 0 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 license-verified contractor leads

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 a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
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.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use 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

## North Carolina Plumbing & HVAC Contractor Leads Scraper

**License-verified plumbing, heating & fire-sprinkler leads, straight from the state board.** Every row is pulled live from the North Carolina State Board of Examiners of Plumbing, Heating & Fire Sprinkler Contractors public registry — company, license number, every active classification (plumbing, HVAC groups, fuel piping, fire sprinkler), expiration date, mailing address, phone, the named classification holders, and the employer behind each licensed technician. Sweep the whole state or target a city, classification, company, or license number. Opt-in contact-email finding turns registry records into outreach-ready leads. JSON or CSV out, billed per lead.

#### How it works

![How NC Plumbing & HVAC Contractor Leads Scraper works](https://raw.githubusercontent.com/muhamed-didovic/muhamed-didovic.github.io/main/assets/how-it-works-nc-plumbing-hvac-leads.png)

#### ✨ Why use this scraper?

- **License-verified, not directory-scraped** — every lead comes from the board's own registry, so license number, classifications, and expiry are authoritative at scrape time.
- **All 24 classifications, cleanly filterable** — Plumbing Class I/II, Heating Groups 1–3 (contractor + technician), Fuel Piping, and Fire Sprinkler lines, each selectable in the input. Multi-classification shops arrive as one row with the full list.
- **People included** — each row carries its *classification holders* (the named individuals the license stands on), and technician Sub-Licenses resolve their **employer** (parent licensee + firm + license number).
- **Active-only by default** — records with no active classifications are dropped before you pay for them.
- **Full-state sweep built in** — run it empty and it walks a bundled list of ~1,500 NC place names, metro areas first. No URL hunting.
- **Opt-in contact emails** — the actor discovers each business's own website (search + directory filtering + name-match guard) and harvests a contact email from it. You're only charged when an email is actually found.
- **New-license monitoring** — flip `monitoringMode` on a schedule and each run returns only contractors licensed since your last run.
- **Zero setup** — US residential proxy is built in; the registry is public, no login or CAPTCHA involved.

#### 🎯 Use cases

| Who | What they do with it |
|---|---|
| **HVAC & plumbing suppliers** | Build call lists of active licensed contractors by city and classification — with phone, address, and the holder names to ask for |
| **SaaS & fintech selling to trades** | Feed license-verified accounts into outbound; `enrichEmails` + `qualifyByPayment` qualify who can actually buy online |
| **Insurance, bonding & lending** | Verify license standing and expiration before quoting |
| **Recruiters** | Map every licensed technician in a metro — each Sub-License row links to its employing firm |
| **GCs & developers** | Verify subs' licenses in bulk from a license-number list before award |

#### 📥 Supported inputs

| Mode | Input | Behaviour |
|---|---|---|
| **Full-state sweep** (default) | *(empty input)* | Walks the bundled NC place list, biggest cities first |
| **City sweep** | `cities: ["Charlotte", "Raleigh"]` | Matches the licensee's mailing-address city |
| **Classification filter** | `classifications: ["Plumbing Class I Contractor"]` | Searched board-side per city, then re-checked on each license |
| **Company search** | `companyNames: ["plumbing"]` | Board-side substring match on company name |
| **Name search** | `lastName` (+ optional `firstName`) | License-holder name lookup |
| **License lookup** | `licenseNumbers: ["L.01098", "S.01622"]` | Direct verification; accepts full numbers or bare digits |
| **Phone lookup** | `phoneNumber` | Phone number on record |

**Not supported:** other states or boards, NC electrical licenses (separate board — see the matching [NC Electrical Contractor Leads Scraper](https://apify.com/memo23/north-carolina-electrical-contractor-leads-scraper)), NC general contractors (NCLBGC).

#### 🔄 How it works

1. **Builds search jobs** from your criteria — or the full-state city list when the input is empty. A classification filter multiplies into city × classification queries, because this board's search results don't label classifications.
2. **Queries the board's live roster** for each job and parses the result rows.
3. **Filters** licenses with no active classifications (default), unwanted classifications, and duplicates — before any per-lead fetching.
4. **Fetches each license's detail record** — company, contact, address, phone, expiry, active classifications, classification holders — and, for Sub-License technicians, the **parent employer** record.
5. **Optionally enriches**: discovers the business's own website and scrapes a contact email; `qualifyByPayment` additionally detects Stripe/e-commerce signals.
6. **Pushes one row per license** to the dataset — holders, classifications, and enrichment merged inline.

#### ⚙️ Input parameters

| Field | Type | Default | Description |
|---|---|---|---|
| `cities` | array | `[]` → full sweep | NC mailing-address cities |
| `classifications` | array | `[]` → all | Any of the board's 24 classifications (label, fragment like `plumbing class i`, or numeric code) |
| `activeOnly` | boolean | `true` | Keep only licenses with ≥1 active classification |
| `companyNames` | array | — | Substring company-name searches |
| `lastName` / `firstName` | string | — | License-holder name search |
| `licenseNumbers` | array | — | Direct license lookups |
| `phoneNumber` | string | — | Phone-number search |
| `enrichEmails` | boolean | `false` | Website discovery + contact-email harvesting |
| `qualifyByPayment` | boolean | `false` | Payment-processor detection (needs `enrichEmails`) |
| `monitoringMode` | boolean | `false` | Only licenses not delivered on previous runs |
| `resetMonitoringState` | boolean | `false` | One-shot: clear the monitoring baseline |
| `maxItems` | integer | `500` | Hard cap on leads |
| `maxConcurrency` | integer | `10` | Parallel detail fetches |
| `searchConcurrency` | integer | `3` | Parallel roster searches |

#### 📊 Output overview

One dataset row per license. The row merges the roster entry, the board's detail record, the classification holders, the parent employer (for Sub-Licenses), and (when enabled) email enrichment — no separate or padding rows. `isActive` reflects whether any classification is active, and `searchCity` records which sweep query surfaced the lead.

#### 📦 Output sample

Real trimmed row from a live run:

```json
{
    "type": "license-lead",
    "stateCode": "NC",
    "trade": "plumbing-hvac-fire-sprinkler",
    "boardId": "ncphfsc",
    "boardName": "North Carolina State Board of Examiners of Plumbing, Heating & Fire Sprinkler Contractors",
    "licenseNumber": "L.01098",
    "classificationCode": "L",
    "classification": "Heating Group 1 - Class I Contractor",
    "activeClassifications": [
        "Heating Group 1 - Class I Contractor",
        "Heating Group 2 Contractor",
        "Heating Group 3 - Class I Contractor",
        "Plumbing Class I Contractor",
        "Residential Fire Sprinkler Installation Contractor"
    ],
    "accountType": "License",
    "businessName": "P. C. Godfrey, Inc.",
    "contactName": "Aaron Reid Ellis",
    "dbaName": null,
    "licenseStatus": "Active",
    "isActive": true,
    "expirationDate": "2026-12-31",
    "phone": "(704) 622-6168",
    "address": {
        "street": "1816 Rozzelles Ferry Rd",
        "city": "Charlotte",
        "state": "NC",
        "zip": "28208",
        "raw": "1816 Rozzelles Ferry Rd, Charlotte, NC 28208"
    },
    "searchCity": "Charlotte",
    "classificationHolders": [
        { "name": "Aaron Reid Ellis", "classification": "Heating Group 3 - Class I Contractor" }
    ],
    "sourceUrl": "https://public.nclicensing.org/Public/Search",
    "scrapedAt": "2026-07-24T06:02:11.101Z"
}
````

Sub-License (technician) rows additionally carry the employer:

```json
{
    "licenseNumber": "S.01622",
    "accountType": "Sub-License",
    "parentLicensees": [
        { "licenseeName": "James Samuel Grahl", "licenseNumber": "L.28697", "status": "Active", "firm": "Embree-Reed, Incorporated" }
    ]
}
```

With `enrichEmails: true`, successful rows also carry `contactEmail`, `contactWebsite`, and the detailed `emailEnrichment` object.

#### 🗂 Key output fields

| Group | Fields |
|---|---|
| **License** | `licenseNumber`, `classification`, `activeClassifications[]`, `classificationCode`, `accountType`, `licenseStatus`, `isActive`, `expirationDate` |
| **Business** | `businessName`, `contactName`, `dbaName`, `phone`, `address.{street,city,state,zip,raw}` |
| **People** | `classificationHolders[]` — the named individuals behind the license; `parentLicensees[]` — a technician's employing firm(s) |
| **Enrichment** (opt-in) | `contactEmail`, `contactWebsite`, `emailEnrichment`, `takesPayments`, `paymentProcessors`, `stripeLiveKey`, `paymentConfidence` |
| **Provenance** | `boardId`, `boardName`, `stateCode`, `trade`, `searchCity`, `sourceUrl`, `scrapedAt` |

#### ❓ FAQ

**How complete is the full-state sweep?**
The board's search matches on each licensee's *mailing-address city*. The bundled sweep list covers ~1,500 NC municipalities, CDPs, and USPS alias spellings, so in-state coverage is effectively complete; out-of-state mailing addresses are only found via company/name/license searches.

**How is "active" decided?**
This board publishes no status field. A license is counted active when its **Active Classifications** list is non-empty — that's the board's own signal for a license in force. `activeOnly: false` returns records without active classifications too.

**What's the difference between License and Sub-License rows?**
`License` (L.\*) accounts are the primary company/contractor licenses — the classic lead. `Sub-License` (S.\*) accounts are individuals working under a licensee; their rows include `parentLicensees` naming the employing firm. Filter on `accountType` downstream if you only want companies.

**What's the email hit rate?**
Roughly a quarter to a third of active companies yield a verified contact email — many small trades shops have no website at all. You're only charged the email event when one is found; misses cost nothing. `emailEnrichment.confidence` and `source` let you filter.

**Does monitoring mode re-charge for old leads?**
No. `monitoringMode` remembers every license already delivered to *your* account and silently skips it on later runs. The first monitored run seeds the baseline.

**Is this legal to scrape?**
The registry is a public government licensing record intended for license verification by the public. The actor accesses only those public pages, politely. See the Disclaimer below for your responsibilities.

#### 💬 Support

- 🐛 **Issues:** [the actor's Issues tab](https://apify.com/memo23/north-carolina-plumbing-hvac-contractor-leads-scraper/issues)
- 💡 **Feature requests:** same place — concrete requests (new fields, other states, other trades) usually ship fast
- 📧 **Direct:** message via [my Apify profile](https://apify.com/memo23)

#### 🛠 Additional services

Need the same license-verified leads pipeline for **another state or trade**, a custom export, or a managed feed into your CRM? This actor is part of a per-state series built on a shared engine — new boards stamp out quickly. Reach out via my Apify profile.

#### 🔎 Explore more scrapers

The matching **[NC Electrical Contractor Leads Scraper](https://apify.com/memo23/north-carolina-electrical-contractor-leads-scraper)** covers the state's electrical board. More lead-gen and directory actors: **[apify.com/memo23](https://apify.com/memo23)** — email finding & verification, business directories, real-estate, and job boards.

#### 🤖 For AI Agents & LLM Apps

This actor is MCP-ready: call it from agent frameworks via the [Apify MCP server](https://mcp.apify.com), or hit it with a single API call and read the dataset as clean JSON. The row schema is stable and flat where it matters (`licenseNumber`, `businessName`, `phone`, `address.city`, `isActive`, `activeClassifications`), so it drops straight into RAG pipelines, lead-qualification agents, and compliance-check tools. Typical agent patterns: *"verify this plumber's NC license"* (pass `licenseNumbers`), *"find active Plumbing Class I contractors in Raleigh with emails"* (pass `cities` + `classifications` + `enrichEmails`), or a scheduled *"alert me on newly licensed HVAC contractors"* loop (`monitoringMode`).

***

### ⚠️ Disclaimer

This Actor is an independent tool and is not affiliated with, endorsed by, or sponsored by the North Carolina State Board of Examiners of Plumbing, Heating & Fire Sprinkler Contractors, the State of North Carolina, or any of their agencies or affiliates. All trademarks mentioned are the property of their respective owners.

The scraper accesses only the board's publicly available license-verification pages — no authenticated endpoints and no content behind a login. License data is a public record published for verification purposes; contact-email enrichment visits only the businesses' own public websites. Users are responsible for ensuring their use complies with applicable law and regulation — including data-protection rules (GDPR, CCPA), telemarketing/e-mail regulations (TCPA, CAN-SPAM) when contacting leads, and any obligations of their own organisation.

***

### SEO Keywords

north carolina plumbing contractor leads, nc hvac contractor leads, nc plumbing license lookup, hvac license verification north carolina, fire sprinkler contractor list nc, licensed plumber database north carolina, nc mechanical contractor leads, plumbing hvac leads scraper, state licensing board scraper, contractor license verify api, home services leads scraper, hvac company email list, newly licensed contractors alerts, contractor license monitoring, charlotte plumbers list, raleigh hvac contractors, heating contractor database nc, fuel piping contractor list, apify contractor leads, b2b trades leads

# Actor input Schema

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

NC cities to sweep, matched EXACTLY against each licensee's mailing-address city (e.g. `Charlotte`, `Winston Salem`). Empty = the bundled full-state place list, biggest cities first.

## `classifications` (type: `array`):

Keep only these classifications (searched board-side, then re-checked against each license's Active Classifications). Empty = all 24. Values may also be typed as a fragment (`plumbing class i`) or the board's numeric code (`16`).

## `activeOnly` (type: `boolean`):

Keep only licenses with at least one Active Classification on file (this board publishes no status field). Turn off for historical or compliance research.

## `companyNames` (type: `array`):

Company-name searches (the board substring-matches, so `plumbing` finds every company containing the word). Overrides the city sweep.

## `lastName` (type: `string`):

Search by the license holder's last name. Overrides the city sweep.

## `firstName` (type: `string`):

Optional, only used together with the last name.

## `licenseNumbers` (type: `array`):

Direct license lookups — accepts `L.00007`, `S.01622`, or bare digits. Overrides every other mode.

## `phoneNumber` (type: `string`):

Search by the phone number on record. Overrides the city sweep.

## `enrichEmails` (type: `boolean`):

Discover each business's website and harvest a contact email from it. Adds `contactEmail`, `contactWebsite`, and the detailed `emailEnrichment` object. Best-effort — no charge on misses.

## `qualifyByPayment` (type: `boolean`):

Piggybacks on email enrichment (only runs when that's on): detects Stripe/Square/e-commerce signals on the business site. Adds `takesPayments`, `paymentProcessors`, `stripeLiveKey`, `paymentConfidence`. Free.

## `monitoringMode` (type: `boolean`):

Skip licenses already delivered to you on previous runs — ideal on a schedule for catching newly licensed contractors. The first run seeds the baseline and returns everything.

## `resetMonitoringState` (type: `boolean`):

One-shot: forget every previously delivered license at run start, so this run returns everything again.

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

Hard cap on leads pushed to the dataset.

## `maxConcurrency` (type: `integer`):

Parallel per-licensee detail fetches.

## `searchConcurrency` (type: `integer`):

Parallel roster searches. Each returns up to ~600 rows; keep modest to stay polite to the board.

## Actor input object example

```json
{
  "cities": [
    "Charlotte",
    "Raleigh",
    "Durham"
  ],
  "classifications": [],
  "activeOnly": true,
  "enrichEmails": false,
  "qualifyByPayment": false,
  "monitoringMode": false,
  "resetMonitoringState": false,
  "maxItems": 500,
  "maxConcurrency": 10,
  "searchConcurrency": 3
}
```

# 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 = {
    "cities": [
        "Charlotte",
        "Raleigh",
        "Durham"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("memo23/north-carolina-plumbing-hvac-contractor-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 = { "cities": [
        "Charlotte",
        "Raleigh",
        "Durham",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("memo23/north-carolina-plumbing-hvac-contractor-leads-scraper").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 '{
  "cities": [
    "Charlotte",
    "Raleigh",
    "Durham"
  ]
}' |
apify call memo23/north-carolina-plumbing-hvac-contractor-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=memo23/north-carolina-plumbing-hvac-contractor-leads-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "North Carolina Plumbing & HVAC Contractor Leads Scraper",
        "description": "Scrape license-verified plumbing, HVAC & fire-sprinkler contractor leads from North Carolina's state licensing board — company, license number, active classifications, expiry, address, phone, classification holders, and technician-employer links. Full-state sweep or targeted search.",
        "version": "0.0",
        "x-build-id": "HVaAuE5qphgCFXA2b"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/memo23~north-carolina-plumbing-hvac-contractor-leads-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-memo23-north-carolina-plumbing-hvac-contractor-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/memo23~north-carolina-plumbing-hvac-contractor-leads-scraper/runs": {
            "post": {
                "operationId": "runs-sync-memo23-north-carolina-plumbing-hvac-contractor-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/memo23~north-carolina-plumbing-hvac-contractor-leads-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-memo23-north-carolina-plumbing-hvac-contractor-leads-scraper",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "properties": {
                    "cities": {
                        "title": "Cities (mailing address)",
                        "type": "array",
                        "description": "NC cities to sweep, matched EXACTLY against each licensee's mailing-address city (e.g. `Charlotte`, `Winston Salem`). Empty = the bundled full-state place list, biggest cities first.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "classifications": {
                        "title": "License classifications",
                        "type": "array",
                        "description": "Keep only these classifications (searched board-side, then re-checked against each license's Active Classifications). Empty = all 24. Values may also be typed as a fragment (`plumbing class i`) or the board's numeric code (`16`).",
                        "items": {
                            "type": "string",
                            "enum": [
                                "Plumbing Class I Contractor",
                                "Plumbing Class II Contractor",
                                "Restricted Limited Plumbing Contractor",
                                "Plumbing Class I Technician",
                                "Plumbing Class II Technician",
                                "Heating Group 1 - Class I Contractor",
                                "Heating Group 1 - Class II Contractor",
                                "Heating Group 2 Contractor",
                                "Heating Group 3 - Class I Contractor",
                                "Heating Group 3 - Class II Contractor",
                                "Heating Group 1 Class I Technician",
                                "Heating Group 1 Class II Technician",
                                "Heating Group 2 Technician",
                                "Heating Group 3 Class I Technician",
                                "Heating Group 3 Class II Technician",
                                "Fuel Piping Contractor",
                                "Fuel Piping Class I Technician",
                                "Fuel Piping Class II Technician",
                                "Fire Sprinkler Installation Contractor",
                                "Fire Sprinkler Inspection Contractor",
                                "Fire Sprinkler Inspection Technician",
                                "Fire Sprinkler Maintenance Technician",
                                "Residential Fire Sprinkler Installation Contractor",
                                "Residential Fire Sprinkler Design Contractor"
                            ]
                        },
                        "default": []
                    },
                    "activeOnly": {
                        "title": "Active licenses only",
                        "type": "boolean",
                        "description": "Keep only licenses with at least one Active Classification on file (this board publishes no status field). Turn off for historical or compliance research.",
                        "default": true
                    },
                    "companyNames": {
                        "title": "Company names",
                        "type": "array",
                        "description": "Company-name searches (the board substring-matches, so `plumbing` finds every company containing the word). Overrides the city sweep.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "lastName": {
                        "title": "Licensee last name",
                        "type": "string",
                        "description": "Search by the license holder's last name. Overrides the city sweep."
                    },
                    "firstName": {
                        "title": "Licensee first name",
                        "type": "string",
                        "description": "Optional, only used together with the last name."
                    },
                    "licenseNumbers": {
                        "title": "License numbers",
                        "type": "array",
                        "description": "Direct license lookups — accepts `L.00007`, `S.01622`, or bare digits. Overrides every other mode.",
                        "items": {
                            "type": "string"
                        }
                    },
                    "phoneNumber": {
                        "title": "Phone number",
                        "type": "string",
                        "description": "Search by the phone number on record. Overrides the city sweep."
                    },
                    "enrichEmails": {
                        "title": "Find contact emails (opt-in)",
                        "type": "boolean",
                        "description": "Discover each business's website and harvest a contact email from it. Adds `contactEmail`, `contactWebsite`, and the detailed `emailEnrichment` object. Best-effort — no charge on misses.",
                        "default": false
                    },
                    "qualifyByPayment": {
                        "title": "Detect payment processors",
                        "type": "boolean",
                        "description": "Piggybacks on email enrichment (only runs when that's on): detects Stripe/Square/e-commerce signals on the business site. Adds `takesPayments`, `paymentProcessors`, `stripeLiveKey`, `paymentConfidence`. Free.",
                        "default": false
                    },
                    "monitoringMode": {
                        "title": "Only new licenses since last run",
                        "type": "boolean",
                        "description": "Skip licenses already delivered to you on previous runs — ideal on a schedule for catching newly licensed contractors. The first run seeds the baseline and returns everything.",
                        "default": false
                    },
                    "resetMonitoringState": {
                        "title": "Reset monitoring baseline",
                        "type": "boolean",
                        "description": "One-shot: forget every previously delivered license at run start, so this run returns everything again.",
                        "default": false
                    },
                    "maxItems": {
                        "title": "Maximum leads",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Hard cap on leads pushed to the dataset.",
                        "default": 500
                    },
                    "maxConcurrency": {
                        "title": "Detail concurrency",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Parallel per-licensee detail fetches.",
                        "default": 10
                    },
                    "searchConcurrency": {
                        "title": "Search concurrency",
                        "minimum": 1,
                        "type": "integer",
                        "description": "Parallel roster searches. Each returns up to ~600 rows; keep modest to stay polite to the board.",
                        "default": 3
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
