# GAF Certified Residential Roofing Contractors (`supplemental_gorse/gaf-certified-roofers`) Actor

Scrape GAF's public Find-a-Contractor directory for residential certified roofers by US state, city, or ZIP. Outputs name, certification tier (e.g. Master Elite), awards, rating, review count, phone, address/city, distance, profile URL.

- **URL**: https://apify.com/supplemental_gorse/gaf-certified-roofers.md
- **Developed by:** [Calvin Torng](https://apify.com/supplemental_gorse) (community)
- **Categories:** Lead generation, Business
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

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

## GAF Certified Residential Roofing Contractors

Get a clean list of **GAF certified residential roofers** for any US city: business name, **GAF tier (Master Elite, Certified Plus, Certified)**, awards such as **President's Club**, star rating, review count, phone, city/state, distance and the GAF profile link. The data comes from GAF's public Find-a-Contractor directory.

**Pairs with [US Hail & Storm Damage Reports](https://apify.com/supplemental_gorse/us-hail-storm-reports)** (Actor ID `SBgvsmhTFKyee4IgC`). Find where hail hit, then find the certified roofers who serve that area.

### Who it's for

- **Roofing suppliers and distributors** building territory lists of GAF-certified installers
- **Storm-marketing agencies and lead brokers** matching hail events to local certified contractors
- **Insurance, public adjusters and restoration networks** vetting contractors by tier, rating and reviews
- **Competitive research**: how many Master Elite roofers sit inside 25 miles of a metro

### Input

| Field | Type | Notes |
|---|---|---|
| `state` | string | 2-letter code, e.g. `TX`. Required unless `startUrls` is set |
| `city` | string | e.g. `Dallas`. GAF searches 25 miles around the city |
| `zip` | string | 5-digit ZIP (experimental) |
| `startUrls` | string\[] | GAF directory URLs to scrape directly; overrides state/city/zip |
| `certificationFilter` | string\[] | `Master Elite`, `Certified Plus`, `Certified`. Empty = all |
| `maxItems` | integer | Default 200 |
| `maxPages` | integer | Result pages per start URL, 10 contractors each. Default 50 |
| `useResidentialProxy` | boolean | Default false. Turn on only if runs get blocked |

```json
{ "state": "TX", "city": "Dallas", "certificationFilter": ["Master Elite"], "maxItems": 200 }
```

### Output

One row per contractor:

```json
{
  "name": "James Kate Roofing & Restoration",
  "certificationTier": "Master Elite",
  "awards": ["President's Club"],
  "rating": 4.9,
  "reviewCount": 312,
  "phone": "(972) 400-4707",
  "address": null,
  "city": "Mansfield",
  "state": "TX",
  "zip": null,
  "distance": 24.1,
  "profileUrl": "https://www.gaf.com/en-us/roofing-contractors/residential/usa/tx/mansfield/james-kate-roofing-restoration-1111566",
  "contractorId": "1111566",
  "sourceUrl": "https://www.gaf.com/en-us/roofing-contractors/residential/usa/tx/dallas?distance=25",
  "scrapedAt": "2026-11-02T09:53:14.668Z"
}
```

- `address` and `zip` are usually `null`, because GAF's directory cards show only city and state.
- Long names can be cut off at about 40 characters on the card. When GAF's page data has the full name, the Actor uses it.
- `awards` lists the distinctions shown on the card. Contractors with many distinctions may have more on their profile page ("View All").

### Pricing

Pay per event: you pay for each **contractor record** saved, and nothing for platform usage. See the Pricing tab for the current price.

### Notes

- The Actor is polite: one browser tab, delays between pages.
- It reads only public directory pages and does not log in.
- Use the data in line with GAF's [Terms of Use](https://www.gaf.com/en-us/about-us/privacy-legal/terms-of-use) and with the laws that apply to you (e.g. TCPA and CAN-SPAM for outreach).
- This Actor is not affiliated with or endorsed by GAF. GAF, Master Elite, Certified Plus and President's Club are trademarks of their owners.

# Actor input Schema

## `state` (type: `string`):

2-letter US state code, e.g. TX. Required unless you provide Start URLs.

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

Optional city name, e.g. Dallas or San Antonio. Leave empty to list the whole state page.

## `zip` (type: `string`):

Optional 5-digit US ZIP. When set, searches near this ZIP instead of the state/city page (experimental).

## `startUrls` (type: `array`):

Optional. GAF directory URLs to scrape directly, e.g. https://www.gaf.com/en-us/roofing-contractors/residential/usa/tx/dallas. When set, State/City/ZIP are ignored.

## `certificationFilter` (type: `array`):

Optional. Keep only contractors with these GAF certification tiers. Empty = keep all.

## `maxItems` (type: `integer`):

Maximum contractor records to save. Keeps cost predictable.

## `maxPages` (type: `integer`):

Maximum directory result pages (or 'Load more' clicks) per start URL.

## `useResidentialProxy` (type: `boolean`):

Off by default (datacenter/automatic proxy). Turn on only if runs get blocked.

## Actor input object example

```json
{
  "state": "TX",
  "city": "Dallas",
  "certificationFilter": [],
  "maxItems": 200,
  "maxPages": 50,
  "useResidentialProxy": false
}
```

# Actor output Schema

## `overview` (type: `string`):

Default dataset items: one row per GAF certified residential roofing contractor.

# 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 = {
    "state": "TX",
    "city": "Dallas"
};

// Run the Actor and wait for it to finish
const run = await client.actor("supplemental_gorse/gaf-certified-roofers").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 = {
    "state": "TX",
    "city": "Dallas",
}

# Run the Actor and wait for it to finish
run = client.actor("supplemental_gorse/gaf-certified-roofers").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 '{
  "state": "TX",
  "city": "Dallas"
}' |
apify call supplemental_gorse/gaf-certified-roofers --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,supplemental_gorse/gaf-certified-roofers"
        }
    }
}
```

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/dWSYXCoNh52asegT4/builds/4REvhsSroFlodb2Ge/openapi.json
