# Arizona Roofing Contractor Directory & Leads (ROC) (`scrapebench/arizona-roofing-contractor-directory-leads-scraper`) Actor

Export every active Arizona ROC-licensed roofing contractor (1,484 on 2026-10-13) with phone, street address, qualifying party, principals, class and expiry date.

- **URL**: https://apify.com/scrapebench/arizona-roofing-contractor-directory-leads-scraper.md
- **Developed by:** [ScrapeBench](https://apify.com/scrapebench) (community)
- **Categories:** Lead generation
- **Stats:** 1 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$0.002 / roofing contractor record

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

## Arizona Roofing Contractor Directory & Leads (ROC)

### Pain points

- ROC's contractor search answers one name at a time, and a search for a roofing class stops at about 1,000 contractors, so the 1,382 CR-42 dual roofers cannot all be listed from it.
- ROC's statewide Posting List has every active licence with a street address but no phone number, and it is a 12 MB CSV of 58,345 licences you re-download and filter down to roofing yourself.
- The phone and the people behind a licence (owners, members, qualifying party) are only on the search side, so a roofer list with both means matching two sources by licence number.

### What we solve

- Export all of Arizona's active roofing licences in one run — classes C-42 (commercial), R-42 (residential) and CR-42 (dual) — or narrow by class, city, ZIP or Arizona-only mailing addresses, each with a batch list.
- Each licence is matched to ROC's contractor search by licence number for its business phone and principals, and every row says whether that match landed.
- Every row carries the street address, qualifying party, issue date and expiry date from ROC's official Posting List.

### Summary

Arizona roofing contractors from the Arizona Registrar of Contractors (ROC). The actor reads ROC's official Posting List — every active licence in the state — keeps the three roofing classes (C-42 commercial, R-42 residential, CR-42 dual; 1,484 licences on the 2026-10-13 list) and adds each licence's business phone and principals from ROC's own contractor search. Filter by class, city, ZIP or Arizona-only mailing addresses. Pay per roofing licence returned.

### Who it's for

- Roofing material suppliers and distributors building Arizona prospect lists
- Sales reps selling insurance, financing, software or equipment to roofing contractors
- General contractors and property managers sourcing licensed roofing subs near a job
- Lead-generation agencies building city- or ZIP-level roofer lists for Phoenix, Tucson and Mesa

### How to use

Set the input, run the actor, and collect results from the run's dataset (export to JSON/CSV/Excel, or pull via the Apify API). Example input:

```json
{
  "city": "Phoenix",
  "maxResults": 500
}
```

See **Inputs** below for every available field.

### What you get

One row per record:

| Field | Description |
|---|---|
| `license_number` | ROC licence number, six digits |
| `business_name` | Licensed business name |
| `dba_name` | Doing-business-as name, when filed |
| `classification` | C-42, R-42 or CR-42 |
| `classification_detail` | Class code and name, e.g. CR-42 Roofing |
| `license_class_type` | Specialty Commercial, Residential or Dual |
| `status` | Always Active |
| `person_name` | Qualifying party |
| `principals` | People on the licence with their roles |
| `phone` | Business phone from ROC's contractor search |
| `address` | Mailing street address |
| `city` | Mailing city |
| `address_state` | Mailing state (out-of-state firms hold ROC licences) |
| `zip_code` | Mailing ZIP |
| `issue_date` | Licence issue date |
| `expiration_date` | Licence expiry date |
| `enrichment_status` | Whether the phone/principals match landed |
| `source_url` | ROC's Posting List page |

Sample:

```json
{
  "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",
  "enrichment_status": "matched",
  "license_type": "Contractor License",
  "state": "AZ",
  "source_url": "https://roc.az.gov/posting-list"
}
```

### Inputs

| Field | Required | Type | Default | Description |
|---|---|---|---|---|
| `classification` | no | string | — | 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` | no | array | `[]` | Several roofing classes in ONE run, e.g. R-42 and CR-42. |
| `city` | no | string | — | 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` | no | array | `[]` | Several cities in ONE run, e.g. Phoenix, Mesa, Scottsdale. |
| `zipCode` | no | string | — | 5-digit mailing ZIP, e.g. '85018'. |
| `zipCodes` | no | array | `[]` | Several ZIP codes in ONE run. |
| `arizonaAddressesOnly` | no | boolean | `false` | 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` | no | boolean | `true` | 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, qualifying party). Adds time, not cost. Rows that could not be matched say so in enrichment_status. |
| `maxResults` | no | integer | `500` | 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. |
| `proxyConfiguration` | no | object | `{"useApifyProxy": true, "apifyProxyGroups": ["RESIDENTIAL"], "apifyProxyCountry": "US"}` | Used only to download ROC's statewide posting list, and only if the direct download is refused (ROC's Cloudflare blocks datacenter IPs). Residential is the setting that works. |

### Pricing (Pay Per Event)

You pay per result (`dataset-item`) — **no charge for empty runs**. Example: **246 Phoenix roofing contractors** at *$0.002/result* ≈ **$0.49**.

The default run (500 licences) costs $1.00; every active roofer in the state (1,484 on 2026-10-13) costs $2.97 at maxResults 100000. Apify platform usage, including residential proxy traffic for the posting-list download, is billed separately.

### Use cases

- Supplier prospecting: a shingle or underlayment distributor exports every roofer with a Phoenix, Mesa or Gilbert mailing address, with phone, for a territory push.
- Statewide list build: a marketing agency takes all 1,484 active roofers once, then re-runs monthly and compares licence numbers and issue dates to catch new licensees.
- Residential vs commercial targeting: a sales team pulls only R-42 residential or C-42 commercial roofers for a campaign that fits one market.
- Sub sourcing: a general contractor lists the roofers in a job's ZIP codes, with qualifying party and expiry date, before sending bid invitations.

### Why this actor

- Phone on 1,478 of the 1,484 active roofing licences, street address on 1,483 and principals on all 1,484, in one statewide run of 244 s (measured 2026-10-14).
- Straight from ROC's two official public sources, read fresh on every run: the Posting List for the active roster and the contractor search for phone and principals.
- A class, city or ZIP it does not recognise is dropped and logged, never silently widened to the whole state; outage and bad-input notices are free.

### Limitations & updates

Active licences only, because ROC's Posting List holds only those; no suspended, expired or revoked roofers. ROC refreshes the list every few days (the 2026-10-14 run read the file dated 2026-10-13), so a brand-new licence can be missing for a few days. ROC files licences by mailing address and the list has no county column, so filter by city or ZIP; those find where the contractor's mail goes, not where they work. No email: ROC publishes none. ROC's Cloudflare refuses Apify's datacenter IPs, so the posting-list download goes through a residential proxy, billed by Apify as platform usage.

### FAQ

**Where does the data come from?**

From the Arizona Registrar of Contractors (ROC). The actor downloads ROC's Posting List (roc.az.gov/posting-list), the official CSV of every active licence, and keeps the roofing classes; phone and principals come from ROC's public contractor search (azroc.my.site.com), matched by licence number. Both are read on every run.

**How many roofing contractors are there?**

1,484 active roofing licences on the 2026-10-13 Posting List: CR-42 Roofing (dual) 1,382, C-42 (commercial) 54, R-42 (residential) 48. Largest mailing cities: Phoenix 246, Tucson 199, Mesa 132, Gilbert 68.

**Which classes count as roofing?**

ROC's three roofing classes: C-42 (commercial), R-42 (residential) and CR-42 (dual — both). Leave the class filter empty for all three, or name one or more to narrow. Any other class is dropped and logged.

**Does it include out-of-state contractors?**

Yes: 184 of the 1,484 active roofing licences have a mailing address outside Arizona, most often California (46), Texas (28) and Utah (19). Tick 'Arizona mailing addresses only' to drop them.

**Why is phone missing on a few rows?**

The Posting List has no phone column, so each licence is matched to ROC's contractor search. All 1,484 matched on 2026-10-14; 6 of those accounts carry no phone. enrichment_status says what happened on each row.

**How am I charged?**

$0.002 per roofing licence returned. The default run returns 500 ($1.00); Phoenix roofers were 246 ($0.49) and the whole state 1,484 ($2.97) on 2026-10-14. Rows explaining an outage, an unrecognised filter or a reached spending cap are free.

### Which actor to choose

Part of the roofing contractor suite — pick the state you need:

- **Illinois Roofing Contractor Directory & Leads Scraper (IDFPR)** — You want Illinois roofing contractors — every IDFPR roofing licence by county.
- **West Virginia Roofing Contractor Directory & Leads (WVCLB)** — You want West Virginia roofing contractors — every current WVCLB roofing licence with phone and mailing address, by county.

### Guides & use cases

Written up on **[scrapebench.dev](https://scrapebench.dev)** — the bench that runs and verifies this actor against the live source every night:

- **How-to:** [How to run Arizona Roofing Contractor Directory & Leads (ROC)](https://scrapebench.dev/guides/how-to-arizona-roofing-contractor-directory-leads-scraper/)
- **Use case:** [Export Every Arizona Roofing Contractor (ROC)](https://scrapebench.dev/use-cases/arizona-roofing-contractors-statewide/)

More actors, coverage and nightly verification results: **[scrapebench.dev](https://scrapebench.dev)**

### Works with AI assistants (MCP)

Callable as an MCP tool, so Claude, Cursor, VS Code Copilot and other MCP clients can run it directly. Grab the config from the [MCP tab](https://apify.com/scrapebench/arizona-roofing-contractor-directory-leads-scraper/api/mcp) on this page — Apify hosts the server and keeps that snippet current, and OAuth signs you in on first connect, so no API token goes in your config file.

Then just ask:

> "List the active Arizona roofing contractors with a Phoenix mailing address, with phone and qualifying party."

That input returned 246 licences on 2026-10-14. With no filters it exports the whole state (1,484 roofers), so set a city or ZIP before running it from an agent if you want fewer. Runs started this way bill exactly like any other run.

# Actor input Schema

## `classification` (type: `string`):

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` (type: `array`):

Several roofing classes in ONE run, e.g. R-42 and CR-42.

## `city` (type: `string`):

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` (type: `array`):

Several cities in ONE run, e.g. Phoenix, Mesa, Scottsdale.

## `zipCode` (type: `string`):

5-digit mailing ZIP, e.g. '85018'.

## `zipCodes` (type: `array`):

Several ZIP codes in ONE run.

## `arizonaAddressesOnly` (type: `boolean`):

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` (type: `boolean`):

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, qualifying party). Adds time, not cost. Rows that could not be matched say so in enrichment_status.

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

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.

## `proxyConfiguration` (type: `object`):

Used only to download ROC's statewide posting list, and only if the direct download is refused (ROC's Cloudflare blocks datacenter IPs). Residential is the setting that works.

## Actor input object example

```json
{
  "classification": "CR-42",
  "classifications": [],
  "city": "Phoenix",
  "cities": [],
  "zipCodes": [],
  "arizonaAddressesOnly": false,
  "includePhone": true,
  "maxResults": 500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}
```

# Actor output Schema

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

Each row is one ROC roofing licence: business, DBA, class, qualifying party, principals, phone, street address and dates.

# 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 = {
    "maxResults": 500,
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ],
        "apifyProxyCountry": "US"
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("scrapebench/arizona-roofing-contractor-directory-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 = {
    "maxResults": 500,
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
        "apifyProxyCountry": "US",
    },
}

# Run the Actor and wait for it to finish
run = client.actor("scrapebench/arizona-roofing-contractor-directory-leads-scraper").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 '{
  "maxResults": 500,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ],
    "apifyProxyCountry": "US"
  }
}' |
apify call scrapebench/arizona-roofing-contractor-directory-leads-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,scrapebench/arizona-roofing-contractor-directory-leads-scraper"
        }
    }
}
```

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/Pwmelr3K3GVy1h9f6/builds/yX7peA9P4EjPgPPVq/openapi.json
