# Arizona ROC Contractor License Scraper (`muhammadafzal/az-roc-contractor-license-scraper`) Actor

Scrape Arizona Registrar of Contractors (AZ ROC) public license records. Search by license number, business name, qualifying party, or city. Returns license status, classifications, bonds, complaints, personnel, address, phone — fast, no login.

- **URL**: https://apify.com/muhammadafzal/az-roc-contractor-license-scraper.md
- **Developed by:** [Muhammad Afzal](https://apify.com/muhammadafzal) (community)
- **Categories:** Lead generation, Real estate
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 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.

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.md):

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

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.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/platform/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

## Arizona ROC Contractor License Scraper

Scrape the Arizona Registrar of Contractors (AZ ROC) public license database for full contractor license records. Search by license number, business name, qualifying party, or city — get status, classifications, bonds, complaints, personnel, and full contact details in structured JSON. Full pagination is supported across every results page. No login required.

### Use cases

- **Contractor verification** — confirm a contractor is licensed and in good standing before hiring or paying a deposit
- **Compliance monitoring** — detect newly expired, suspended, or revoked licenses for vendor lists on recurring schedules
- **Lead generation** — build targeted lists of licensed Arizona contractors by city, classification, or active status
- **Subcontractor sourcing** — find qualified subcontractors by specialty classification (B-1, C-37, R-11, CR-39) and region
- **Insurance underwriting** — score contractors using complaint history, bond status, and surety coverage
- **Due diligence** — check complaint and disciplinary records before signing contracts
- **Market research** — analyze contractor density by city/classification, track new-license issuance trends
- **Consumer protection** — surface complaint counts and outcomes for homeowner pre-payment checks

### Input

The actor supports four search modes. Use any one or combine multiple in a single run:

| Field | Type | Required | Default | Description |
|-------|------|----------|---------|-------------|
| `licenseNumbers` | array | no | `[]` | Direct license-number lookup (fastest). Example: `["333282", "123456"]` |
| `companyNames` | array | no | `[]` | Business names (partial match supported). Example: `["Desert HVAC", "Acme Plumbing"]` |
| `qualifyingPartyNames` | array | no | `[]` | Qualifying party names. Example: `["John Smith"]` |
| `cities` | array | no | `[]` | Arizona cities for region-based search. Example: `["Phoenix", "Tucson", "Scottsdale"]` |
| `licenseType` | select | no | `"ALL"` | `ALL`, `RESIDENTIAL`, `COMMERCIAL`, `DUAL` |
| `licenseStatus` | select | no | `"ALL"` | `ALL`, `ACTIVE`, `SUSPENDED`, `EXPIRED`, `REVOKED`, `CANCELLED` |
| `licenseClassification` | string | no | `""` | Filter by AZ ROC classification code (e.g. `"B-1"`, `"C-37"`, `"R-11"`, `"CR-39"`) |
| `scrapeDetailPage` | boolean | no | `false` | Open each detail page for bond, complaint, and personnel data (slower) |
| `scrapeComplaints` | boolean | no | `false` | Include per-complaint records (requires `scrapeDetailPage`) |
| `maxResults` | integer | no | `100` | Hard cap on records returned across all search jobs. `0` = unlimited |
| `resultsPerPage` | select | no | `"50"` | `10`, `20`, or `50` (50 minimises pagination) |
| `maxConcurrency` | integer | no | `3` | Parallel browser tabs. Keep ≤ 5 for residential proxies |
| `customProxyUrl` | string | no | `""` | Premium residential proxy URL for maximum reliability |
| `proxyConfiguration` | object | no | residential | Apify proxy settings |

**At least one search mode is required.**

#### Example inputs

**License number lookup:**
```json
{
    "licenseNumbers": ["333282", "123456"],
    "maxResults": 2
}
````

**Business name search:**

```json
{
    "companyNames": ["Desert HVAC"],
    "licenseStatus": "ACTIVE",
    "maxResults": 50
}
```

**City lead generation with classification filter:**

```json
{
    "cities": ["Phoenix", "Tucson"],
    "licenseClassification": "C-37",
    "licenseStatus": "ACTIVE",
    "maxResults": 200
}
```

**Full enrichment with bonds and complaints:**

```json
{
    "companyNames": ["Desert HVAC"],
    "scrapeDetailPage": true,
    "scrapeComplaints": true,
    "maxResults": 25
}
```

### Output

Each record contains the full public record for one contractor license. When `scrapeDetailPage` is `false` (default), the fast list view populates license number, business name, qualifying party, classification, status, city, and phone. When `true`, bond details, complaint history, all classifications, and personnel are also collected.

| Field | Type | Description |
|-------|------|-------------|
| `license_number` | string | AZ ROC license number |
| `business_name` | string | Legal business name |
| `dba_name` | string|null | Doing-business-as name |
| `qualifying_party` | string|null | Individual responsible for the license |
| `license_status` | string | Active, Suspended, Expired, Revoked, or Cancelled |
| `license_type` | string|null | Residential, Commercial, or Dual (inferred from classification) |
| `entity_type` | string|null | CORPORATION, LLC, SOLE OWNER, etc. (detail only) |
| `primary_classification` | string | Primary classification code |
| `classification_description` | string|null | Primary classification description (detail only) |
| `classifications` | array | All classifications `[{code, description}]` (detail only) |
| `city` | string|null | Business address city |
| `state` | string|null | Business address state |
| `zip` | string|null | Business address ZIP (detail only) |
| `business_phone` | string|null | Business phone, normalized `(XXX) XXX-XXXX` |
| `issued_date` | string|null | Original license issue date (ISO 8601, detail only) |
| `renewed_through_date` | string|null | License valid-through date (ISO 8601, detail only) |
| `bond_company` | string|null | Surety company (detail only) |
| `bond_number` | string|null | Bond identifier (detail only) |
| `bond_type` | string|null | Bond type (detail only) |
| `bond_amount` | string|null | Bond coverage amount (detail only) |
| `bond_status` | string|null | Bond status (detail only) |
| `bond_effective_date` | string|null | Bond effective date (ISO 8601, detail only) |
| `bond_expiration_date` | string|null | Bond expiration date (ISO 8601, detail only) |
| `complaint_count` | number | Total complaints shown (prior two years, detail only) |
| `open_cases` | number|null | Open complaint cases (detail only) |
| `resolved_cases` | number|null | Resolved complaint cases (detail only) |
| `disciplined_cases` | number|null | Disciplined complaint cases (detail only) |
| `complaints` | array|null | Per-complaint records `[{date, type, status, summary}]` (when `scrapeComplaints` enabled) |
| `personnel` | array | Personnel `[{name, position}]` (detail only) |
| `profile_url` | string | Direct AZ ROC profile link |
| `scraped_at` | string | ISO 8601 timestamp |
| `source_url` | string | Source page URL |

### Pricing

| Event | Price |
|-------|-------|
| Actor Start | $0.00005 |
| License Record | **$0.003** per record |

**~$3.00 per 1,000 records.** You only pay for records successfully scraped. Start with a small `maxResults` to test before scaling. Both PAY\_PER\_EVENT and usage-based (compute + proxy passthrough) monetization are enabled.

### Technical notes

- **Residential proxy recommended**: The AZ ROC portal is a Salesforce Experience Cloud (LWC/Aura) site with bot protection that can challenge datacenter IP pools. The actor uses Apify residential proxy by default. For production bulk runs, provide a premium residential proxy via `customProxyUrl` (Decodo/Bright Data/IPRoyal) for maximum reliability.
- **Salesforce LWC rendering**: The portal uses Lightning Web Components which render asynchronously. The actor waits for network idle plus a buffer, and uses multi-selector fallbacks on every field to survive LWC DOM changes between releases.
- **Pagination**: Full Next-button pagination is supported. The actor stops after 3 consecutive scrolls with no new results or at a 5,000-record safety cap per search.
- **Detail-page enrichment**: By default the actor uses the fast list view (one page load per search + per-record list extraction). Enable `scrapeDetailPage` to open each contractor's detail page for bond, complaint, and personnel data — roughly doubles runtime.
- **Classification codes**: See the [AZ ROC classification list](https://roc.az.gov/license-classifications) for all specialty codes. License type is inferred from the classification prefix: `B-*` → Commercial, `R-*` → Residential, `C-*` → Commercial, `CR-*` → Dual.
- **Deduplication**: Records are deduplicated by license number within a single run.

### Integration

Export scraped data, run the scraper via API, schedule and monitor runs, or integrate with other tools. The output dataset is compatible with:

- CSV/JSON/Excel export from Apify Console
- Zapier and Make webhooks
- Direct API access via Apify REST API
- CRM import (HubSpot, Salesforce) with field mapping
- AI agent tool calls via Apify MCP server — the actor exposes a single `license-record` event with structured JSON output suitable for LLM consumption

# Actor input Schema

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

One or more AZ ROC license numbers to look up directly (e.g. '123456'). Fastest and most precise search method — direct single-record lookup, no pagination. Leading zeros are handled automatically. Use this field when the user provides exact license numbers or a list of licenses to verify. Do NOT use this when the user describes a trade, region, or company name — use companyNames, qualifyingPartyNames, or cities instead.

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

Business names to search (e.g. 'Desert HVAC', 'Acme Plumbing'). Partial matches are supported by the AZ ROC portal. Each name triggers a separate search and may return multiple results across multiple pages — all are traversed automatically. Use this field when the user provides company names. Do NOT use this for license-number lookups — use licenseNumbers instead.

## `qualifyingPartyNames` (type: `array`):

Full name of the qualifying party (the individual responsible for the license). Format: 'First Last' (e.g. 'John Smith'). Partial names are accepted by the AZ ROC portal. Use this field when the user provides a person's name rather than a company name. Do NOT use this for business-name searches — use companyNames instead.

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

Arizona city names to search by location (e.g. 'Phoenix', 'Tucson', 'Scottsdale', 'Mesa'). Each city triggers a separate search and may return thousands of records across many pages — all are traversed. Combine with licenseType, licenseStatus, or licenseClassification filters to narrow. Use this field when the user wants contractors by region or for building territory-based lead lists. Do NOT use this for a single license lookup — use licenseNumbers instead.

## `licenseType` (type: `string`):

Filter results by license type. 'ALL' returns all types. 'RESIDENTIAL' covers home construction/repair. 'COMMERCIAL' covers non-residential projects. 'DUAL' covers both. Applied as a client-side filter on the AZ ROC portal's results. Use this to narrow city or company searches to a license category.

## `licenseStatus` (type: `string`):

Filter by license status. 'ALL' returns every record regardless of standing. 'ACTIVE' returns only contractors currently authorized to work — the common choice for lead-generation and insurance use cases. Applied post-scrape since the AZ ROC portal uses a single text-search field.

## `licenseClassification` (type: `string`):

Optional classification code to filter results (e.g. 'B-1', 'R-11', 'C-37', 'CR-39'). Leave blank to include all classifications. Find all codes at https://roc.az.gov/license-classifications. Applied post-scrape — the portal's search is text-based and may return broader results, which are then narrowed in the output.

## `scrapeDetailPage` (type: `boolean`):

When enabled, the scraper opens each contractor's detail page to collect complete information including bond details, all license classifications, complaint history, and qualifying party details. This is much slower (one extra page load per contractor), so it is OFF by default — the fast list view already returns license number, business name, qualifying party, classification, status, city, phone, and profile URL. Turn this ON only when you need bond and complaint data.

## `scrapeComplaints` (type: `boolean`):

When enabled (requires Scrape Full Detail Page), fetches the per-complaint records (date, type, status, summary) in addition to the aggregate complaint count. Only complaints from the prior two years are shown on the AZ ROC portal. OFF by default to keep runs fast and cheap.

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

Hard cap on the total number of records pushed to the dataset across all search jobs. Protects against accidental large runs — billing is per record pushed. Start low (10-50) to validate output quality before scaling up. Set to 0 for unlimited (use with caution on city searches, which can return thousands of records and unexpected billing).

## `resultsPerPage` (type: `string`):

Number of rows per DOM page on the AZ ROC portal. Valid values are 10, 20, or 50 (as offered by the native page-size selector). 50 minimises the number of Next clicks required and is fastest.

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

Maximum number of browser tabs running in parallel. Higher values speed up bulk runs but increase memory and proxy usage. Recommended: 2-5 for residential proxies. Keep ≤ 5 to avoid triggering Salesforce bot protection.

## `customProxyUrl` (type: `string`):

Optional premium residential proxy URL (e.g. 'http://user:pass@proxy.decodo.com:10000'). The AZ ROC portal uses Salesforce bot protection which can challenge Apify's shared datacenter and residential IPs. For production bulk runs, provide your own premium residential proxy (Decodo/Bright Data/IPRoyal) for maximum reliability. If left blank, the actor uses Apify residential proxy automatically.

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

Apify proxy settings. Residential proxy is strongly recommended — the AZ ROC portal uses Salesforce bot protection which can challenge datacenter IP pools.

## Actor input object example

```json
{
  "licenseNumbers": [
    "333282"
  ],
  "companyNames": [],
  "qualifyingPartyNames": [],
  "cities": [],
  "licenseType": "ALL",
  "licenseStatus": "ALL",
  "licenseClassification": "",
  "scrapeDetailPage": false,
  "scrapeComplaints": false,
  "maxResults": 10,
  "resultsPerPage": "50",
  "maxConcurrency": 3,
  "customProxyUrl": "",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}
```

# Actor output Schema

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

Link to the dataset containing all extracted AZ ROC contractor records.

# 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 = {
    "licenseNumbers": [
        "333282"
    ],
    "companyNames": [],
    "qualifyingPartyNames": [],
    "cities": [],
    "licenseType": "ALL",
    "licenseStatus": "ALL",
    "licenseClassification": "",
    "scrapeDetailPage": false,
    "scrapeComplaints": false,
    "maxResults": 10,
    "resultsPerPage": "50",
    "maxConcurrency": 3,
    "customProxyUrl": "",
    "proxyConfiguration": {
        "useApifyProxy": true,
        "apifyProxyGroups": [
            "RESIDENTIAL"
        ]
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/az-roc-contractor-license-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 = {
    "licenseNumbers": ["333282"],
    "companyNames": [],
    "qualifyingPartyNames": [],
    "cities": [],
    "licenseType": "ALL",
    "licenseStatus": "ALL",
    "licenseClassification": "",
    "scrapeDetailPage": False,
    "scrapeComplaints": False,
    "maxResults": 10,
    "resultsPerPage": "50",
    "maxConcurrency": 3,
    "customProxyUrl": "",
    "proxyConfiguration": {
        "useApifyProxy": True,
        "apifyProxyGroups": ["RESIDENTIAL"],
    },
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/az-roc-contractor-license-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 '{
  "licenseNumbers": [
    "333282"
  ],
  "companyNames": [],
  "qualifyingPartyNames": [],
  "cities": [],
  "licenseType": "ALL",
  "licenseStatus": "ALL",
  "licenseClassification": "",
  "scrapeDetailPage": false,
  "scrapeComplaints": false,
  "maxResults": 10,
  "resultsPerPage": "50",
  "maxConcurrency": 3,
  "customProxyUrl": "",
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": [
      "RESIDENTIAL"
    ]
  }
}' |
apify call muhammadafzal/az-roc-contractor-license-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=muhammadafzal/az-roc-contractor-license-scraper",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Arizona ROC Contractor License Scraper",
        "description": "Scrape Arizona Registrar of Contractors (AZ ROC) public license records. Search by license number, business name, qualifying party, or city. Returns license status, classifications, bonds, complaints, personnel, address, phone — fast, no login.",
        "version": "1.0",
        "x-build-id": "uwuS6io5To1wTTToq"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/muhammadafzal~az-roc-contractor-license-scraper/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-muhammadafzal-az-roc-contractor-license-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/muhammadafzal~az-roc-contractor-license-scraper/runs": {
            "post": {
                "operationId": "runs-sync-muhammadafzal-az-roc-contractor-license-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/muhammadafzal~az-roc-contractor-license-scraper/run-sync": {
            "post": {
                "operationId": "run-sync-muhammadafzal-az-roc-contractor-license-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": {
                    "licenseNumbers": {
                        "title": "License Numbers",
                        "type": "array",
                        "description": "One or more AZ ROC license numbers to look up directly (e.g. '123456'). Fastest and most precise search method — direct single-record lookup, no pagination. Leading zeros are handled automatically. Use this field when the user provides exact license numbers or a list of licenses to verify. Do NOT use this when the user describes a trade, region, or company name — use companyNames, qualifyingPartyNames, or cities instead.",
                        "items": {
                            "type": "string"
                        },
                        "default": [
                            "333282"
                        ]
                    },
                    "companyNames": {
                        "title": "Company / Business Names",
                        "type": "array",
                        "description": "Business names to search (e.g. 'Desert HVAC', 'Acme Plumbing'). Partial matches are supported by the AZ ROC portal. Each name triggers a separate search and may return multiple results across multiple pages — all are traversed automatically. Use this field when the user provides company names. Do NOT use this for license-number lookups — use licenseNumbers instead.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "qualifyingPartyNames": {
                        "title": "Qualifying Party Names",
                        "type": "array",
                        "description": "Full name of the qualifying party (the individual responsible for the license). Format: 'First Last' (e.g. 'John Smith'). Partial names are accepted by the AZ ROC portal. Use this field when the user provides a person's name rather than a company name. Do NOT use this for business-name searches — use companyNames instead.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "cities": {
                        "title": "Cities",
                        "type": "array",
                        "description": "Arizona city names to search by location (e.g. 'Phoenix', 'Tucson', 'Scottsdale', 'Mesa'). Each city triggers a separate search and may return thousands of records across many pages — all are traversed. Combine with licenseType, licenseStatus, or licenseClassification filters to narrow. Use this field when the user wants contractors by region or for building territory-based lead lists. Do NOT use this for a single license lookup — use licenseNumbers instead.",
                        "items": {
                            "type": "string"
                        },
                        "default": []
                    },
                    "licenseType": {
                        "title": "License Type",
                        "enum": [
                            "ALL",
                            "RESIDENTIAL",
                            "COMMERCIAL",
                            "DUAL"
                        ],
                        "type": "string",
                        "description": "Filter results by license type. 'ALL' returns all types. 'RESIDENTIAL' covers home construction/repair. 'COMMERCIAL' covers non-residential projects. 'DUAL' covers both. Applied as a client-side filter on the AZ ROC portal's results. Use this to narrow city or company searches to a license category.",
                        "default": "ALL"
                    },
                    "licenseStatus": {
                        "title": "License Status Filter",
                        "enum": [
                            "ALL",
                            "ACTIVE",
                            "SUSPENDED",
                            "EXPIRED",
                            "REVOKED",
                            "CANCELLED"
                        ],
                        "type": "string",
                        "description": "Filter by license status. 'ALL' returns every record regardless of standing. 'ACTIVE' returns only contractors currently authorized to work — the common choice for lead-generation and insurance use cases. Applied post-scrape since the AZ ROC portal uses a single text-search field.",
                        "default": "ALL"
                    },
                    "licenseClassification": {
                        "title": "License Classification",
                        "type": "string",
                        "description": "Optional classification code to filter results (e.g. 'B-1', 'R-11', 'C-37', 'CR-39'). Leave blank to include all classifications. Find all codes at https://roc.az.gov/license-classifications. Applied post-scrape — the portal's search is text-based and may return broader results, which are then narrowed in the output.",
                        "default": ""
                    },
                    "scrapeDetailPage": {
                        "title": "Scrape Full Detail Page",
                        "type": "boolean",
                        "description": "When enabled, the scraper opens each contractor's detail page to collect complete information including bond details, all license classifications, complaint history, and qualifying party details. This is much slower (one extra page load per contractor), so it is OFF by default — the fast list view already returns license number, business name, qualifying party, classification, status, city, phone, and profile URL. Turn this ON only when you need bond and complaint data.",
                        "default": false
                    },
                    "scrapeComplaints": {
                        "title": "Include Complaint History",
                        "type": "boolean",
                        "description": "When enabled (requires Scrape Full Detail Page), fetches the per-complaint records (date, type, status, summary) in addition to the aggregate complaint count. Only complaints from the prior two years are shown on the AZ ROC portal. OFF by default to keep runs fast and cheap.",
                        "default": false
                    },
                    "maxResults": {
                        "title": "Max Results (total)",
                        "minimum": 0,
                        "maximum": 1000000,
                        "type": "integer",
                        "description": "Hard cap on the total number of records pushed to the dataset across all search jobs. Protects against accidental large runs — billing is per record pushed. Start low (10-50) to validate output quality before scaling up. Set to 0 for unlimited (use with caution on city searches, which can return thousands of records and unexpected billing).",
                        "default": 100
                    },
                    "resultsPerPage": {
                        "title": "Results Per Page",
                        "enum": [
                            "10",
                            "20",
                            "50"
                        ],
                        "type": "string",
                        "description": "Number of rows per DOM page on the AZ ROC portal. Valid values are 10, 20, or 50 (as offered by the native page-size selector). 50 minimises the number of Next clicks required and is fastest.",
                        "default": "50"
                    },
                    "maxConcurrency": {
                        "title": "Max Concurrent Browsers",
                        "minimum": 1,
                        "maximum": 20,
                        "type": "integer",
                        "description": "Maximum number of browser tabs running in parallel. Higher values speed up bulk runs but increase memory and proxy usage. Recommended: 2-5 for residential proxies. Keep ≤ 5 to avoid triggering Salesforce bot protection.",
                        "default": 3
                    },
                    "customProxyUrl": {
                        "title": "Custom Proxy URL",
                        "type": "string",
                        "description": "Optional premium residential proxy URL (e.g. 'http://user:pass@proxy.decodo.com:10000'). The AZ ROC portal uses Salesforce bot protection which can challenge Apify's shared datacenter and residential IPs. For production bulk runs, provide your own premium residential proxy (Decodo/Bright Data/IPRoyal) for maximum reliability. If left blank, the actor uses Apify residential proxy automatically.",
                        "default": ""
                    },
                    "proxyConfiguration": {
                        "title": "Proxy Configuration",
                        "type": "object",
                        "description": "Apify proxy settings. Residential proxy is strongly recommended — the AZ ROC portal uses Salesforce bot protection which can challenge datacenter IP pools."
                    }
                }
            },
            "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
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
