# Roofing Contractor Lead Scraper — Verified Emails (`muhammadafzal/roofing-contractor-lead-scraper`) Actor

Find roofing contractors by niche and location. Extract phones, websites, services, socials, and public emails with conservative DNS, SMTP, and catch-all checks. Free-plan runs return at most 5 records; paid plans can request up to 200.

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

## Pricing

from $35.00 / 1,000 roofing contractor leads

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?

Actors are web data automations that power AI and operations. They run on the Apify platform to scrape websites, process data, connect APIs, and automate workflows.
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.

- **AI agents and MCP clients** — the [Apify MCP server](https://docs.apify.com/integrations/mcp.md) at `https://mcp.apify.com` (remote, streamable HTTP, OAuth on first use).
- **Agentic workflows and local Actor development** — [Agent Skills](https://apify.com/.well-known/agent-skills/index.json) with the [Apify CLI](https://docs.apify.com/cli/docs.md): `npm install -g apify-cli`, then `apify login`.
- **JavaScript/TypeScript projects** — the official [JS/TS client](https://docs.apify.com/api/client/js/docs.md): `npm install apify-client`.
- **Python projects** — the official [Python client](https://docs.apify.com/api/client/python/docs.md): `pip install apify-client`.
- **Any other language** — 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

Find roofing contractors by city, state, ZIP code, or custom niche query. The Actor searches live Google Maps listings, visits each contractor's public website, extracts business contact details and roofing services, and checks email deliverability using DNS, SMTP recipient acceptance, and catch-all detection.

**Free-plan limit:** Apify free-plan users receive at most 5 records per run. Upgrade to an Apify paid plan to request up to 200 records per run.

Use the dataset for roofing software sales, material supplier outreach, recruiting, territory planning, agency prospecting, insurance vendor research, and local market analysis. Results are available as JSON, CSV, Excel, XML, RSS, or through the Apify API and integrations.

### What you get

Each result can include:

- Business name, Maps category, address, phone, website, hours, rating, review count, coordinates, Maps URL, and place identifier
- Public email addresses with the exact source page where each address was found
- A conservative email verification result: `smtp_verified`, `catch_all`, `mx_only`, `smtp_rejected`, `invalid_domain`, `unknown`, or `not_checked`
- Detected roofing services, including residential, commercial, repair, replacement, shingles, metal, flat/TPO/EPDM, tile, and new-roof installation
- Public Facebook, Instagram, LinkedIn, and X/Twitter links found on the company website

The Actor never sends an email. It only performs mail-routing and SMTP handshake checks when verification is enabled.

### Input

| Field | Type | Default | Description |
|---|---|---|---|
| `businessType` | string | `Roofing Contractor` | Roofing niche or `Custom` |
| `location` | string | `Dallas, TX` | City, state, postal code, neighborhood, or region |
| `searchQuery` | string | empty | Exact Maps query when `businessType` is `Custom` |
| `maxResults` | integer | `25` | Maximum leads to deliver, from 1 to 200; free-plan runs are capped at 5 |
| `enrichEmails` | boolean | `true` | Find public business emails on contractor websites |
| `verifyEmails` | boolean | `true` | Run DNS, SMTP, and catch-all checks |
| `emailRequirement` | string | `any` | Keep any contractor, only leads with a found email, or only `smtp_verified` emails |
| `enrichSocials` | boolean | `true` | Find public social profile links |
| `proxyConfiguration` | object | Apify Proxy | Network settings for Maps requests |

The Console prefill requests only 3 results so you can inspect quality and cost before scaling.

#### Example input

```json
{
  "businessType": "Storm Damage Roofer",
  "location": "Denver, CO",
  "maxResults": 25,
  "enrichEmails": true,
  "verifyEmails": true,
  "emailRequirement": "smtp_verified",
  "enrichSocials": true,
  "proxyConfiguration": {
    "useApifyProxy": true,
    "apifyProxyGroups": ["RESIDENTIAL"]
  }
}
```

### Example output

```json
{
  "business_name": "Example Roofing & Exteriors",
  "business_type": "Storm Damage Roofer",
  "category": "Roofing contractor",
  "services": ["Roof Repair", "Roof Replacement", "Storm Damage"],
  "phone": "+1 303-555-0142",
  "email": "estimates@example-roofing.com",
  "email_verified": true,
  "email_verification_status": "smtp_verified",
  "email_verification_note": "SMTP accepted this mailbox and rejected a random mailbox",
  "email_source_url": "https://example-roofing.com/contact",
  "all_emails": [
    {
      "address": "estimates@example-roofing.com",
      "verified": true,
      "status": "smtp_verified",
      "verificationNote": "SMTP accepted this mailbox and rejected a random mailbox",
      "sourceUrl": "https://example-roofing.com/contact"
    }
  ],
  "website": "https://example-roofing.com/",
  "address": "100 Main St, Denver, CO 80202",
  "rating": 4.8,
  "review_count": 214,
  "hours": "Open · Closes 5 PM",
  "facebook": "https://facebook.com/example-roofing",
  "instagram": null,
  "linkedin": null,
  "twitter": null,
  "google_maps_url": "https://www.google.com/maps/place/example",
  "place_id": "0x123:0x456",
  "latitude": 39.7392,
  "longitude": -104.9903,
  "scraped_at": "2026-09-12T12:00:00.000Z",
  "source_url": "https://www.google.com/maps/search/storm%20damage%20roofing%20contractor%20in%20Denver%2C%20CO?hl=en"
}
```

### Understanding email verification

Email verification is deliberately conservative:

- `smtp_verified`: the receiving server accepted the discovered mailbox and rejected a generated random mailbox on the same domain.
- `catch_all`: the server accepts random recipients, so the specific mailbox cannot be confirmed.
- `mx_only`: the domain has mail routing, but SMTP probing was blocked, timed out, or remained inconclusive.
- `smtp_rejected`: the receiving server definitively rejected the mailbox.
- `invalid_domain`: invalid syntax or no MX records.
- `not_checked`: verification was disabled.

An SMTP result is a point-in-time signal, not a guarantee that future mail will be delivered. Mail servers may greylist, block verification traffic, or accept mail before rejecting it later. Always follow applicable privacy, marketing, anti-spam, and data-protection laws.

### Pricing

Pay per event keeps cost predictable:

- Apify free-plan users can receive up to 5 saved records per run. Paid-plan users can request up to 200.

- Actor start: `$0.00005` per automatically calculated start event

- Roofing contractor lead: `$0.035` per saved dataset record

Approximate event prices are $0.35 for 10 leads, $1.75 for 50 leads, and $3.50 for 100 leads, plus the small start event. The Actor saves each result before charging its lead event and stops cleanly when the run's maximum charge is reached.

### API example

```javascript
import { ApifyClient } from 'apify-client';

const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('muhammadafzal/roofing-contractor-lead-scraper').call({
  businessType: 'Commercial Roofer',
  location: 'Houston, TX',
  maxResults: 20,
  enrichEmails: true,
  verifyEmails: true
});

const { items } = await client.dataset(run.defaultDatasetId).listItems();
console.log(items);
```

### Reliability and limits

- Google Maps and contractor websites change and may rate-limit automated access. Apify residential proxies are recommended for production.
- Some listings do not include a website, and some websites publish only a contact form or phone number. In those cases email fields are `null` or empty; the Actor does not invent addresses.
- Websites are limited to the home page plus up to two same-domain contact, about, team, service, estimate, or roofing pages.
- SMTP port 25 may be filtered by a network or receiving server, producing the honest `mx_only` result.
- `emailRequirement=smtp_verified` is intentionally strict and may return zero leads when mail servers block probes or use catch-all routing.
- Google Maps result availability varies by geography and query. Run separate locations for broader coverage.
- Diagnostics and outcome classification are saved in the `OUTPUT` key-value record. A run distinguishes `DATA`, `EMPTY`, `BLOCKED`, `REJECTED`, and `FAILED` outcomes.

### Responsible use

Collect and use only lawful public business information. Respect website terms, robots policies where applicable, opt-outs, applicable privacy laws, and email-marketing rules such as CAN-SPAM, CASL, PECR, and GDPR. Do not use this Actor to harass individuals, send deceptive messages, or build sensitive-person profiles.

# Actor input Schema

## `businessType` (type: `string`):

Choose the roofing niche to search. Select Custom to supply an exact Maps query.

## `location` (type: `string`):

City, state, postal code, or region, such as Dallas, TX. Ignored for a Custom query.

## `searchQuery` (type: `string`):

Exact Google Maps query used only when businessType is Custom, for example solar roof installers in Austin, TX.

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

Maximum unique roofing contractor records to deliver. Apify free-plan runs are capped at 5 delivered records; paid-plan runs can request up to 200. Start with 3 to review quality before scaling.

## `enrichEmails` (type: `boolean`):

Visit each contractor website and relevant contact, about, and service pages to find published business email addresses.

## `verifyEmails` (type: `boolean`):

Check syntax, DNS mail routing, SMTP recipient acceptance, and catch-all behavior without sending email. SMTP results can remain unknown when a mail server blocks probes.

## `emailRequirement` (type: `string`):

Choose any to keep every contractor, found to keep only leads with a public website email, or smtp\_verified to keep only individually verified non-catch-all mailboxes.

## `enrichSocials` (type: `boolean`):

Extract public Facebook, Instagram, LinkedIn, and X/Twitter profile links from contractor websites.

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

Apify Proxy settings for Google Maps and website requests. Residential proxy groups are recommended for stable production runs.

## Actor input object example

```json
{
  "businessType": "Roofing Contractor",
  "location": "Dallas, TX",
  "maxResults": 3,
  "enrichEmails": true,
  "verifyEmails": true,
  "emailRequirement": "any",
  "enrichSocials": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}
```

# Actor output Schema

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

No description

## `summary` (type: `string`):

No description

# 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 = {
    "businessType": "Roofing Contractor",
    "location": "Dallas, TX",
    "maxResults": 3,
    "enrichEmails": true,
    "verifyEmails": true,
    "emailRequirement": "any",
    "enrichSocials": true,
    "proxyConfiguration": {
        "useApifyProxy": true
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("muhammadafzal/roofing-contractor-lead-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 = {
    "businessType": "Roofing Contractor",
    "location": "Dallas, TX",
    "maxResults": 3,
    "enrichEmails": True,
    "verifyEmails": True,
    "emailRequirement": "any",
    "enrichSocials": True,
    "proxyConfiguration": { "useApifyProxy": True },
}

# Run the Actor and wait for it to finish
run = client.actor("muhammadafzal/roofing-contractor-lead-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 '{
  "businessType": "Roofing Contractor",
  "location": "Dallas, TX",
  "maxResults": 3,
  "enrichEmails": true,
  "verifyEmails": true,
  "emailRequirement": "any",
  "enrichSocials": true,
  "proxyConfiguration": {
    "useApifyProxy": true
  }
}' |
apify call muhammadafzal/roofing-contractor-lead-scraper --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,muhammadafzal/roofing-contractor-lead-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/KlNzcG0IBq1wiyzGE/builds/tG1FRPld1zXiAsPKc/openapi.json
