# Virginia Contractor License Lookup (DPOR Bulk) (`muhammadafzal/virginia-contractor-license-lookup`) Actor

Download official Virginia DPOR contractor bulk lists. Filter Class A, B, C, or temporary contractor licenses by name, exact license number, city, or state, then export normalized records.

- **URL**: https://apify.com/muhammadafzal/virginia-contractor-license-lookup.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Lead generation
- **Stats:** 3 total users, 2 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## Virginia Contractor License Lookup (DPOR Bulk)

Download Virginia contractor license records from the official Virginia Department of Professional and Occupational Regulation (DPOR) bulk lists. Filter the Class A, Class B, Class C, or temporary contractor lists by business/person name, exact license number, city, and state, then export normalized records from the Apify dataset.

### Data and fields

DPOR provides free ASCII tab-delimited regulant lists and says the lists are updated every five business days. The Actor reads the current links from DPOR's [Regulant Lists](https://www.dpor.virginia.gov/RegulantLists) page, downloads only the selected contractor list files, and records each file's `Last-Modified` value when DPOR supplies it. The Actor uses these contractor source codes:

| DPOR code | List |
| --- | --- |
| `2701`, `2705a` | Class A Contractor |
| `2705b` | Class B Contractor |
| `2705c` | Class C Contractor |
| `2703` | Temporary Contractor |

The source lists identify licensees and may include business email and address information. DPOR notes that phone numbers are not included. The Actor does not add inferred contact details.

Each dataset item contains:

| Fields | Meaning |
| --- | --- |
| `licenseNumber`, `licenseType`, `licenseTypes`, `licenseRank` | Certificate number, DPOR list class, any classes merged for the same certificate, and the source rank value |
| `businessName`, `individualName` | DPOR business or individual name where present |
| `address`, `addressLine1`, `addressLine2`, `poBox`, `city`, `state`, `zipCode`, `zipCodeExtension`, `province`, `country`, `postalCode` | Address fields from the bulk list; ZIP and certificate numbers remain text to preserve leading zeroes |
| `expirationDate`, `certificationDate`, `specialties` | DPOR dates and specialty codes |
| `email` | Published DPOR email value, or `null` if absent or omitted |
| `sourceFileCode`, `sourceListCodes`, `sourceUrl`, `sourceListUrl`, `sourceFileLastModified`, `scrapedAt` | Source file identity, official source URLs, update header, and retrieval time |

### Input

The prefilled run downloads the Class A files and returns up to 100 records. For a broader bulk export, add the other classes:

```json
{
  "licenseTypes": ["class_a", "class_b", "class_c"],
  "nameFilters": [],
  "licenseNumbers": [],
  "cityFilter": "RICHMOND",
  "stateFilter": "VA",
  "maxResults": 1000,
  "includeEmail": true
}
```

`licenseTypes` accepts `class_a`, `class_b`, `class_c`, and `temporary`. Name filters are case-insensitive substrings and match if any provided filter occurs in a business or individual name. `licenseNumbers` uses exact text matching and preserves leading zeroes. `cityFilter` and `stateFilter` are exact case-insensitive matches. Filters are applied to the selected local DPOR bulk files; they are not queries sent to DPOR's interactive License Lookup site.

`maxResults` accepts 1–50,000 unique records. A verified Apify free-plan run is limited to five delivered business records. The Actor does not treat an unknown local account-plan value as a free plan. `includeEmail: false` sets the output's `email` field to `null` while keeping the dataset shape stable.

### Output example

```json
{
  "licenseNumber": "030496",
  "licenseType": "Class A Contractor",
  "licenseTypes": ["Class A Contractor"],
  "licenseRank": "A",
  "businessName": "EXAMPLE CONSTRUCTION LLC",
  "individualName": null,
  "address": "100 MAIN ST, RICHMOND, VA, 23220",
  "addressLine1": "100 MAIN ST",
  "addressLine2": null,
  "poBox": null,
  "city": "RICHMOND",
  "state": "VA",
  "zipCode": "23220",
  "zipCodeExtension": "0000",
  "province": null,
  "country": null,
  "postalCode": null,
  "expirationDate": "08/31/2027",
  "certificationDate": "08/31/1987",
  "specialties": ["CBC", "RBC"],
  "email": "licensing@example.com",
  "sourceFileCode": "2705a",
  "sourceListCodes": ["2705a"],
  "sourceUrl": "https://www.dpor.virginia.gov/sites/default/files/Records%20and%20Documents/Regulant%20List/2705a__crnt.txt",
  "sourceListUrl": "https://www.dpor.virginia.gov/RegulantLists",
  "sourceFileLastModified": "Mon, 21 Sep 2026 12:42:31 GMT",
  "scrapedAt": "2026-09-24T12:00:00.000Z",
  "warnings": []
}
```

`OUTPUT` in the run's key-value store summarizes selected source files, failed files, malformed rows, duplicates, free-plan limits, and item counts. If DPOR omits a selected file or a file is temporarily unavailable, the Actor reports a blocked or partial result and does not create placeholder records. Rows whose tab-delimited field count does not match the header are counted and skipped rather than mapped to potentially incorrect columns.

### Pricing

Pay per event uses one automatic Actor-start event per run and one automatic `apify-default-dataset-item` event for each dataset record. No event is charged for a record that is not written. Platform-usage pass-through is disabled; the run does not add a separate platform-usage event to the user's total.

| Event | FREE | BRONZE | SILVER | GOLD, PLATINUM, DIAMOND |
| --- | ---: | ---: | ---: | ---: |
| Actor start, once per run | $0.005 | $0.005 | $0.005 | $0.005 |
| Contractor license record, per dataset item | $0.005 | $0.004875 | $0.00475 | $0.004 |

Representative event totals, before any plan-specific treatment of event charges:

| Run | FREE | BRONZE | SILVER | GOLD, PLATINUM, DIAMOND |
| --- | ---: | ---: | ---: | ---: |
| Start plus 1 record | $0.010 | $0.009875 | $0.00975 | $0.009 |
| Start plus 100 records | $0.505 | $0.4925 | $0.48 | $0.405 |
| Start plus 1,000 records | $5.005 | $4.88 | $4.755 | $4.005 |

The Actor honors Apify's `maxTotalChargeUsd` run limit. Increase `maxResults` gradually and choose a run spending limit that matches the number of records you expect.

### When to use this Actor

Use it for repeatable Virginia contractor-license research, compliance prechecks, territory analysis, market sizing, licensed-contractor lead lists, and bulk CSV or JSON exports. Use the DPOR source URLs in each record to verify important licensing decisions against the current official source.

Use the separate [Virginia DPOR Professional License Scraper](https://apify.com/muhammadafzal/virginia-dpor-professional-license-scraper) when you need interactive DPOR text or exact license-number lookup across professions beyond contractors. This Actor is limited to the contractor regulant lists above; it does not return tradesman, architect, real-estate, or other DPOR professions.

### API example

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

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('muhammadafzal/virginia-contractor-license-lookup').call({
  licenseTypes: ['class_a', 'class_b', 'class_c'],
  cityFilter: 'RICHMOND',
  maxResults: 250,
  includeEmail: true,
});
const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Reliability and responsible use

The source is a DPOR bulk publication, not a live license certification response. DPOR says its online information can be delayed or incomplete and disclaims warranties about accuracy and completeness. The public lists are refreshed on a schedule, so a record can change after the downloaded file's timestamp. Confirm current status with DPOR before making a legal, compliance, hiring, or payment decision.

Use published names, addresses, and emails only for lawful purposes. Minimize retention and outreach, and follow applicable privacy, consumer-protection, and email laws. This independent Actor is not affiliated with or endorsed by the Commonwealth of Virginia or DPOR.

# Actor input Schema

## `licenseTypes` (type: `array`):

Choose which official DPOR contractor lists to download. Use one or more of class\_a, class\_b, class\_c, or temporary; example: \["class\_a", "class\_b", "class\_c"]. Defaults to Class A only for a quick prefilled run. This does not include tradesmen or other DPOR professions.

## `nameFilters` (type: `array`):

Use for a local case-insensitive substring filter on the downloaded names. Example: \["cornerstone"]. Any filter may match the business or individual name. Leave empty for no name filtering; this is not a DPOR search query.

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

Use to keep only exact DPOR certificate numbers from the selected bulk lists. Example: \["030496"]. Values are compared as text so leading zeroes are preserved; leave empty to skip this filter.

## `cityFilter` (type: `string`):

Use for an exact case-insensitive city match in the DPOR address fields. Example: "RICHMOND". Leave empty for all cities; partial city names are not supported.

## `stateFilter` (type: `string`):

Use for an exact two-letter state or territory code in the DPOR address fields. Example: "VA". Leave empty for all addresses; contractor mailing addresses may be outside Virginia.

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

Maximum unique contractor license records to write after filters. Default 100; allowed range 1–50,000. Free-plan runs are capped at 5 records.

## `includeEmail` (type: `boolean`):

Include the email value published in DPOR's bulk list. Use false to omit it from output. DPOR does not include phone numbers in these lists.

## Actor input object example

```json
{
  "licenseTypes": [
    "class_a"
  ],
  "nameFilters": [],
  "licenseNumbers": [],
  "cityFilter": "",
  "stateFilter": "",
  "maxResults": 100,
  "includeEmail": true
}
```

# Actor output Schema

## `summary` (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 = {
    "licenseTypes": [
        "class_a"
    ],
    "nameFilters": [],
    "licenseNumbers": [],
    "cityFilter": "",
    "stateFilter": "",
    "maxResults": 100,
    "includeEmail": true
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/virginia-contractor-license-lookup").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 = {
    "licenseTypes": ["class_a"],
    "nameFilters": [],
    "licenseNumbers": [],
    "cityFilter": "",
    "stateFilter": "",
    "maxResults": 100,
    "includeEmail": True,
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/virginia-contractor-license-lookup").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 '{
  "licenseTypes": [
    "class_a"
  ],
  "nameFilters": [],
  "licenseNumbers": [],
  "cityFilter": "",
  "stateFilter": "",
  "maxResults": 100,
  "includeEmail": true
}' |
apify call muhammadafzal/virginia-contractor-license-lookup --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/virginia-contractor-license-lookup"
        }
    }
}
```

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/XhJ1ZeeDocX5UpHyo/builds/fzmfakvuzol9klms6/openapi.json
