How-to
How to run West Virginia Roofing Contractor Directory & Leads (WVCLB)
Step-by-step: the exact input, what each field does, and the rows that come back from the source portal.
You want West Virginia’s licensed roofers as a list, with phone numbers. The WV Contractor Licensing Board’s public search has the data, but it cannot give you that list: Roofing is a sub-class of Specialty, Specialty is on 17,377 of the board’s 17,495 licences (2026-10-12), and the classification menu has no roofing option. Free, you’d page through a county 20 contractors at a time with no roofing filter to apply and no export at the end. This actor returns the 1,224 licences that carry the Roofing classification, or the slice of them you filter to.
What you’ll need
Nothing but an Apify account. The actor talks to the source portal directly.
No key, no login and no proxy. The board’s search answered Apify’s own IPs when we measured it on 2026-10-12, so the proxy setting stays off unless a run reports that the board refused it.
Step 1 — Open the actor
Open West Virginia Roofing Contractor Directory & Leads (WVCLB) on the Apify Store and press Try for free. The actor’s own page on this site lists its live health and pricing.
Step 2 — Fill in the input
| Field | Type | Required | What it does |
|---|---|---|---|
county | string | no | A West Virginia county (‘Kanawha’, ‘Berkeley County’), or ‘Out of State’ / ‘International’ for the board’s non-WV licences. A value that is not one of these is dropped and logged. |
counties | array | no | Several counties in ONE run, e.g. Kanawha, Putnam, Cabell. (default []) |
city | string | no | Mailing-address city, exact match ignoring case and punctuation, e.g. ‘Charleston’ (which does not include South Charleston). |
cities | array | no | Several cities in ONE run. (default []) |
westVirginiaAddressesOnly | boolean | no | Out-of-state firms hold WV roofing licences too: 372 of the 1,224 roofing licences (2026-10-14) have a mailing address outside West Virginia. Tick to drop them. |
maxResults | integer | no | Cap on licences for the WHOLE run — your spend control. 1,224 roofing licences statewide on 2026-10-14; set 100000 to take every roofing licence that matches. |
Leave every filter empty and you get the whole state. county and counties narrow it: Kanawha had
64 roofing licences on 2026-10-14, Monongalia 54, Wood 48. A county name the actor doesn’t recognise
is dropped and logged, and the run tells you so in a free notice row instead of quietly exporting all
1,224. Values inside one filter are OR’d and different filters are AND’d, so county: Kanawha with
city: Huntington asks for Huntington mail inside Kanawha, which is almost certainly an empty set.
westVirginiaAddressesOnly is the box first-timers miss. 372 of the 1,224 roofing licences have a
mailing address outside the state, most often Ohio (110) and Virginia (64), and they stay in the
export unless you tick it. The board files 370 of them under the county OUT OF STATE, so a
statewide run with no tick includes them. city matches the mailing city exactly: Charleston
does not include South Charleston.
A working input:
{
"county": "Kanawha",
"maxResults": 500
}
Step 3 — Run it
Press Start. Rows are pushed to the dataset as they’re found, so the run log fills in as it works.
The Kanawha input above took 13 seconds and returned 64 licences on 2026-10-14. A statewide run is slower, about 4 minutes (224 s measured), because the board cannot be asked for roofers: the actor reads all 17,538 current licences and keeps the ones whose classifications include Roofing. A quiet log for a few minutes on a no-filter run is that read, not a hang.
An empty result usually means a city spelled differently from the board’s mailing city, or two filters that don’t overlap. Drop the city, keep the county, and narrow back down.
Step 4 — Read the output
Each run returns a labelled table, not raw JSON:
Every row looks like this — a real row from a real run:
{
"license_number": "WV000232",
"business_name": "HARRIS BROTHERS ROOFING COMPANY",
"dba_name": null,
"phone": "(304) 343-5566",
"address": "1533 HANSFORD STREET",
"city": "CHARLESTON",
"address_state": "WV",
"zip_code": "25311-1917",
"county": "KANAWHA",
"classification": "GENERAL BUILDING; HVAC; PLUMBING; SPECIALTY; ROOFING",
"classifications": [
"GENERAL BUILDING",
"HVAC",
"PLUMBING",
"SPECIALTY",
"ROOFING"
],
"expiration_date": "2027-08-02",
"matched_by": "export",
"status": null,
"license_type": "Contractor License",
"state": "WV",
"source_url": "https://wvclboard.wv.gov/verify/"
}
| Field | Example value | Meaning |
|---|---|---|
license_number | WV000232 | WVCLB licence number, e.g. WV000232 |
business_name | HARRIS BROTHERS ROOFING COMPANY | Licensed company (or sole proprietor) |
dba_name | — | Doing-business-as name, when filed |
phone | (304) 343-5566 | Business phone as the board lists it |
address | 1533 HANSFORD STREET | Mailing street address (can be a PO box) |
city | CHARLESTON | Mailing city |
address_state | WV | Mailing state (out-of-state firms hold WV licences) |
zip_code | 25311-1917 | Mailing ZIP or ZIP+4 |
phone is the reason most people run this, and every one of the 1,224 roofing licences had one on
2026-10-14. The address is a mailing address, and 221 of them are PO boxes, so it tells you where
the contractor’s mail goes rather than where the shop is.
classifications is the column worth a second look. Harris Brothers in the sample also holds
General Building, HVAC and Plumbing, and a siding or gutter supplier can filter on that list to find
roofers who already do their trade. status is always empty: the board’s search lists current
licences only and publishes no status field, so every row is current by construction.
Step 5 — Export it
Open the Dataset tab and export to CSV, JSON, or Excel — or pull the same rows from the API, which is what you want if this is going to run on a schedule.
What it costs
$0.002 per roofing licence returned. Kanawha County was 64 licences, $0.13. The default cap of 500
costs $1.00, and every roofing licence in the state (1,224, with maxResults set to 100,000) costs
$2.45. Rows explaining an outage, an unrecognised county or a reached spending cap are free. Apify
platform usage is billed separately.
Where the data comes from
This reads the source portal directly. Every night a canary runs this actor against that live source and diffs the result against a frozen fixture — what “verified” means.
The source is the West Virginia Contractor Licensing Board’s public search at wvclboard.wv.gov/verify/, which the board says is updated in real time from its licensing database. The search is fine for checking one contractor and useless for listing a trade it doesn’t index: Roofing hides under Specialty, so finding 1,224 roofers means reading 17,538 licences. That is the whole reason a statewide run takes minutes rather than seconds.
See it used
Export Every West Virginia Roofing Contractor (WVCLB) takes all 1,224 licences in one run and re-runs it monthly to catch new licence numbers.
If you need trades other than roofing, or want to look a contractor up by licence number, use West Virginia Contractor License Lookup & Leads Scraper (WVCLB). It reads the same board across all 17,495 current licences, at $0.004 a row.
Last updated 2026-10-14