How-to
How to run Arizona Roofing Contractor Directory & Leads (ROC)
Step-by-step: the exact input, what each field does, and the rows that come back from the source portal.
You want Arizona’s licensed roofers with a phone number next to each one, maybe for a supply territory in Phoenix and Mesa, maybe for a statewide call list. The Arizona Registrar of Contractors (ROC) has both halves, in two places. Its contractor search has the phone and the people on a licence but answers one name at a time, and a search by roofing class stops at about 1,000 contractors, short of the 1,382 CR-42 dual roofers alone. Its Posting List has every active licence with a street address but no phone, as a 12 MB CSV of 58,345 licences across all trades. This actor joins the two by licence number and returns only the 1,484 roofing licences.
What you’ll need
Nothing but an Apify account. The actor talks to the source portal directly.
One thing to know about: a proxy. ROC’s Posting List sits behind a Cloudflare challenge that refuses Apify’s datacenter IPs, so the actor tries a direct download first and then fetches the file through an Apify residential proxy, which is already set as the default in the input. You do not configure anything, but that proxy traffic is Apify platform usage on your account, billed separately from the per-row price. If you switch the proxy off and the direct download is refused, the run returns one free notice row saying so rather than an empty dataset.
Step 1 — Open the actor
Open Arizona Roofing Contractor Directory & Leads (ROC) 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 |
|---|---|---|---|
classification | string | no | Narrow to one ROC roofing class: ‘C-42’ (commercial), ‘R-42’ (residential) or ‘CR-42’ (dual). Leave empty for all three. Any other class is dropped and logged. |
classifications | array | no | Several roofing classes in ONE run, e.g. R-42 and CR-42. (default []) |
city | string | no | Mailing-address city, exact match ignoring case and punctuation, e.g. ‘Phoenix’. ROC lists licences by mailing address, not by where the contractor works. |
cities | array | no | Several cities in ONE run, e.g. Phoenix, Mesa, Scottsdale. (default []) |
zipCode | string | no | 5-digit mailing ZIP, e.g. ‘85018’. |
zipCodes | array | no | Several ZIP codes in ONE run. (default []) |
arizonaAddressesOnly | boolean | no | Out-of-state firms hold ROC roofing licences too: 184 of the 1,484 active roofers (2026-10-13 list) have a mailing address outside Arizona. Tick to drop them. |
includePhone | boolean | no | ROC’s posting list has no phone column. When ticked, each licence is matched to ROC’s contractor search for its phone and the people on the licence (owners, officers,… (default True) |
maxResults | integer | no | Cap on licences for the WHOLE run — your spend control. 1,484 active roofing licences statewide on the 2026-10-13 list; set 100000 to take everything that matches. |
maxResults is a cap on the whole run, prefilled at 500. Arizona had 1,484 active roofing licences
on the 2026-10-13 list, so for the whole state set it to 100000. It is also your spend control: 500
rows is $1.00.
city and zipCode are the fields people misread. ROC files licences by mailing address, and the
list has no county column, so a city filter finds roofers whose mail goes to Phoenix, not roofers who
work there. A Scottsdale crew with a Phoenix PO box shows up under Phoenix. The city match is exact
apart from case and punctuation, and a class, city or ZIP the actor does not recognise is dropped and
logged rather than widened to the whole state. Tick arizonaAddressesOnly if out-of-state firms are
noise to you; 184 of the 1,484 active roofers have a mailing address outside Arizona.
A working input:
{
"city": "Phoenix",
"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 example input returned 246 Phoenix licences on 2026-10-14. A statewide run of all 1,484 took
244 seconds that day, including the match of every licence against ROC’s contractor search for its
phone and principals. Untick includePhone and the run skips that match; it saves time, not money,
since the price per row is the same.
An empty run usually means the city is spelled differently from the mailing address ROC holds, or a
ZIP outside the list. Run once without filters with maxResults at 50 and read the city
column to see how ROC writes it.
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": "234021",
"business_name": "Arcadia Roofing, LLC",
"dba_name": null,
"classification": "CR-42",
"classification_detail": "CR-42 Roofing",
"license_class_type": "Specialty Dual",
"status": "Active",
"person_name": "Tommy Louis Leale",
"principals": [
"Tommy Louis Leale (Member;Qualifying Party)"
],
"phone": "(602) 840-7663",
"address": "3008 N. 44th Street",
"city": "Phoenix",
"address_state": "AZ",
"zip_code": "85018",
"issue_date": "2007-06-16",
"expiration_date": "2027-06-30",
"matched_by": "export",
"license_type": "Contractor License",
"state": "AZ",
"source_url": "https://roc.az.gov/posting-list"
}
| Field | Example value | Meaning |
|---|---|---|
license_number | 234021 | ROC licence number, six digits |
business_name | Arcadia Roofing, LLC | Licensed business name |
dba_name | — | Doing-business-as name, when filed |
classification | CR-42 | C-42, R-42 or CR-42 |
classification_detail | CR-42 Roofing | Class code and name, e.g. CR-42 Roofing |
license_class_type | Specialty Dual | Specialty Commercial, Residential or Dual |
status | Active | Always Active |
person_name | Tommy Louis Leale | Qualifying party |
phone is what most people came for, because the Posting List does not have it. On the 2026-10-14
run all 1,484 licences matched ROC’s contractor search and 1,478 came back with a phone; the other 6
accounts carry none at ROC. principals lists the people on the licence with their roles, which is how
you find the owner rather than only the qualifying party in person_name.
classification splits the market: on the 2026-10-13 list CR-42 dual roofers were 1,382, C-42
commercial 54 and R-42 residential 48. status is always Active, because the Posting List holds only
active licences. There is no email field; ROC publishes none.
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. The Phoenix example, 246 rows, costs $0.49. A run at the prefilled 500 costs $1.00, and every active roofer in the state, 1,484 rows on 2026-10-14, costs $2.97. Rows explaining an outage, an unrecognised filter or a reached spending cap are free. Apify platform usage is billed separately, and on this actor that includes the residential proxy traffic for the Posting List download.
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 roster is ROC’s Posting List at roc.az.gov/posting-list, and the phone and principals come from ROC’s public contractor search at azroc.my.site.com, both read fresh on every run. Neither is a roofer list on its own. The Posting List is a statewide CSV of every trade with no phone column, it answers Apify’s datacenter IPs with a Cloudflare refusal, and ROC refreshes it every few days (the 2026-10-14 run read the file dated 2026-10-13). The search has the contact data but no way to export it, so a list with both means matching 1,484 licence numbers across two sites.
See it used
Export Every Arizona Roofing Contractor (ROC) takes all 1,484 roofers in one run and turns them into a call list.
If your territory includes Illinois, Illinois Roofing Contractor Directory & Leads Scraper (IDFPR) exports IDFPR’s roofing licences by county, with city and ZIP but no phone.
Last updated 2026-10-14