# New Building Permit Leads: Roofing, Solar, HVAC & Contractors (`nerolabs/building-permit-leads`) Actor

New building permits from 7 US city open data portals (NYC, LA, Chicago, SF, Austin, Seattle, New Orleans), sorted by trade (roofing, solar, HVAC, pool, new build) and scored 0 to 100 with job value and contractor. $0.01 per permit. Agent-ready: MCP.

- **URL**: https://apify.com/nerolabs/building-permit-leads.md
- **Developed by:** [Adam Pearce](https://apify.com/nerolabs) (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 $7.00 / 1,000 scored permit leads

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.
Since this Actor supports Apify Store discounts, the price gets lower the higher subscription plan you have.

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

## New Building Permit Leads: Roofing, Solar, HVAC & Contractors

**Who just got a permit to build, re-roof, add solar or replace a boiler in your city?** This actor reads the official building permit feeds of **7 big US cities** (New York City, Los Angeles, Chicago, San Francisco, Austin, Seattle and New Orleans) and returns the permits issued in the last few days as **scored sales leads**, all in one clean format: address, job value, what the work is, the trade it belongs to, the contractor on the job (and their phone where the city publishes it) and the owner where published.

Roofers, solar installers, HVAC and plumbing firms, pool builders, window and door companies, building material suppliers, equipment rental, dumpster and portable toilet hire, insurance agents and subcontractors chasing work all want the same thing: the job, the address and the contractor, the week the permit is issued. Each city publishes its permits in a different shape. This actor puts them in one shape, sorts every permit by trade and ranks them 0 to 100 so the biggest, freshest jobs come first.

### What you get for each permit

- **Lead score 0 to 100** with plain reasons: job value, job size (new build, addition, remodel, roof, solar), how many days since it was issued, contractor named, phone on file, housing units added.
- **Trade**: `mainTrade` plus every trade the job touches, from 19: new construction, ADU, addition or remodel, roofing, solar, HVAC, plumbing and gas, electrical, EV charger, generator, pool and spa, demolition, windows and doors, foundation, fire sprinkler, elevator, fence, signs, scaffold and sidewalk shed. It reads the permit type first (an Austin "Mechanical Permit" is HVAC) and the work description second.
- **The job**: permit number, issue date, permit type, work class, full work description, declared job value in USD, residential or commercial, housing units added.
- **Where**: street address, ZIP (where the city gives one), latitude and longitude.
- **Who**: contractor company, contractor name, contractor phone and licence trade (Austin publishes phones; NYC, Chicago, Seattle and New Orleans publish contractor names), owner name (NYC, Chicago, New Orleans).
- **Link** to the city's permit record where one exists.

The run summary (free, in the `SUMMARY` record) counts matches per city and per trade and lists the **25 busiest contractors** in your results with their permit count and total job value: a ready target list for suppliers.

#### Sample row (shortened)

```json
{
  "leadScore": 85,
  "leadReasons": "Job value $2,113,470. New construction: many trades and suppliers needed. Issued this week. Contractor named on the permit.",
  "mainTrade": "new_construction",
  "trades": ["new_construction", "addition_remodel"],
  "city": "New York City",
  "state": "NY",
  "permitNumber": "B00000000-I1-GC",
  "issuedDate": "2026-11-02",
  "daysSinceIssued": 2,
  "permitType": "General Construction",
  "description": "New 10 story, cellar and sub cellar, community facility development",
  "jobValueUsd": 2113470,
  "address": "105-31 65 Road, Queens",
  "zip": "11375",
  "contractorCompany": "EXAMPLE CONSTRUCTION GROUP",
  "contractorName": "Jane Example",
  "ownerName": "Example Owner LLC",
  "contractorNamed": true
}
```

### How to use it

1. Pick your **cities** (empty means all 7) and how many days to look back (`withinDays`, 1 to 90).
2. Pick your **trades**, for example `["roofing", "solar"]` for a solar installer or `["new_construction", "addition_remodel"]` for a lumber yard.
3. Narrow it: residential or commercial, minimum and maximum job value, ZIP codes, words the work must mention (`"tankless"`, `"metal roof"`, `"restaurant"`) or must not mention.
4. Sell to contractors? Turn on `requireContractor` and upload your customer list as a CSV or Google Sheet (`excludeListUrl`, first column = contractor names or permit numbers) so existing customers are skipped and never charged.
5. Turn on **monitor mode** and schedule it daily: each run returns only permits you have not seen, a fresh lead feed every morning. Add a webhook to push the summary into Zapier, Make, n8n or Slack.

Examples:

- Solar installer in LA and San Francisco: `{"cities": ["los_angeles", "san_francisco"], "trades": ["roofing", "addition_remodel"], "propertyType": "residential", "withinDays": 2, "monitorMode": true}` (a new roof is the best time to add panels)
- Building supplier in Chicago: `{"cities": ["chicago"], "trades": ["new_construction"], "minJobValue": 250000, "requireContractor": true}`
- Pool builder in Austin: `{"cities": ["austin"], "trades": ["pool"], "withinDays": 30}`
- HVAC firm in NYC: `{"cities": ["nyc"], "trades": ["hvac"], "keywords": ["boiler"], "withinDays": 7}`

### Pricing

Pay per event: **$0.01 per scored permit returned** (lower on higher Apify plans), plus a tiny actor start fee. You are charged only for permits that pass your filters; permits skipped by your customer list, monitor mode or filters cost nothing, and the summary is free. 1,000 fresh permit leads cost about $10. Use `maxLeads` or a maximum charge per run to cap spend.

### Cities, sources and limits

| City | Source (official open data portal) | Contractor | Phone | Owner |
|---|---|---|---|---|
| New York City | NYC DOB NOW: Build Approved Permits | yes | no | yes |
| Los Angeles | LADBS Building Permits Issued | no | no | no |
| Chicago | City of Chicago Building Permits | yes | no | yes |
| San Francisco | DataSF Building Permits | no | no | no |
| Austin | Austin Issued Construction Permits | yes | yes | no |
| Seattle | Seattle Building Permits | sometimes | no | no |
| New Orleans | New Orleans Permits | yes | no | yes |

- Every feed is the city's own published open data; nothing is scraped. Cities update daily or weekly, so the newest one or two days can still be filling in (Chicago often lags a few days).
- Some cities leave the job value blank or put 1 (Austin trade permits often do); those show as `null` and are dropped only when you set `minJobValue`.
- NYC issues one permit per trade on a job (general construction, plumbing, mechanical), so a big job can appear once per trade.
- At most 15,000 permits per city per run are read (newest first); shorten `withinDays` if a warning says so.
- Names are public record as published by each city. Use them for business outreach only and follow the rules where you sell (TCPA for calls and texts, CAN-SPAM for email, do-not-call lists).
- City of Chicago: "This site provides applications using data that has been modified for use from its original source, www.cityofchicago.org, the official website of the City of Chicago. The City of Chicago makes no claims as to the content, accuracy, timeliness, or completeness of any of the data provided at this site. The data provided at this site is subject to change at any time. It is understood that the data provided at this site is being used at one's own risk."

More cities are added only when their feed is checked fresh. Ask in the Issues tab for yours.

### For AI agents

Call it through the Apify MCP server (`nerolabs/building-permit-leads`). Inputs are plain fields (`cities`, `withinDays`, `trades`, `minJobValue`, `maxLeads`); output is one JSON row per permit with the fields above and a `SUMMARY` record (counts per city and trade, busiest contractors) in the default key-value store.

### Related Nero Labs actors

- [New Trucking Company Leads](https://apify.com/nerolabs/new-trucking-company-leads): new US trucking companies, scored as leads.
- [Federal Contract Winner Lead Scorer](https://apify.com/nerolabs/federal-contract-winner-leads): companies that just won US federal contracts.
- [Google Sheets Export & Import](https://apify.com/nerolabs/google-sheets-export): send these leads straight into a Sheet.

# Actor input Schema

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

Which city permit feeds to read: chicago, nyc, los_angeles, san_francisco, austin, seattle, new_orleans. Empty means all 7. Each comes from that city's official open data portal and is updated daily or weekly.

## `withinDays` (type: `integer`):

Permits issued in the last N days (1 to 90). Use 1 or 2 with monitor mode for a daily feed.

## `trades` (type: `array`):

Only permits that touch one of these trades, read from the permit type and work description (a kitchen remodel with new wiring counts for addition_remodel and electrical). Empty means every permit.

## `propertyType` (type: `string`):

Residential or commercial, from the city's own class where it gives one, otherwise from the permit text. 'any' keeps permits whose type is unknown.

## `minJobValue` (type: `integer`):

Skip permits with a declared job value below this, e.g. 25000. Permits with no stated value are dropped when this is above 0.

## `maxJobValue` (type: `integer`):

Skip permits above this value. Empty or 0 means no limit.

## `keywords` (type: `array`):

Keep only permits whose type or description contains at least one of these words, e.g. 'tankless', 'metal roof', 'restaurant'. Case does not matter.

## `excludeKeywords` (type: `array`):

Drop permits whose text contains any of these words, e.g. 'repair', 'temporary'.

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

Only permits in these 5 digit ZIP codes. Chicago and New Orleans feeds carry no ZIP, so they drop out when this is set.

## `requireContractor` (type: `boolean`):

Keep only permits that name the contractor (for suppliers who sell to contractors). Chicago, NYC, Austin, Seattle and New Orleans name contractors; LA and San Francisco feeds do not.

## `excludeContractors` (type: `array`):

Contractor company names or permit numbers to leave out, for example your existing customers. Names match loosely (Inc, LLC and case ignored).

## `excludeListUrl` (type: `string`):

Public CSV or Google Sheet link (shared as 'Anyone with the link can view'); the first column holds contractor names or permit numbers to skip, such as your CRM export.

## `minLeadScore` (type: `integer`):

Only return permits scoring at least this (0 to 100). The score adds job value, job size (new build, remodel, roof, solar), how recent, and whether a contractor, phone and owner are named.

## `maxLeads` (type: `integer`):

The best scored permits up to this number are returned and charged; the rest are counted in the summary only.

## `monitorMode` (type: `boolean`):

Remember permits already returned under 'watchName' and return only new ones on later runs. Schedule it daily for a fresh lead feed.

## `watchName` (type: `string`):

Name for this saved watch in monitor mode, so different searches keep separate memories (e.g. 'austin-roofing').

## `webhookUrl` (type: `string`):

POST the run summary (counts per city and trade, busiest contractors) here when the run ends. In monitor mode it is sent only when there are new permits.

## Actor input object example

```json
{
  "cities": [
    "chicago",
    "nyc",
    "los_angeles",
    "san_francisco",
    "austin",
    "seattle",
    "new_orleans"
  ],
  "withinDays": 7,
  "trades": [
    "roofing",
    "solar",
    "hvac",
    "new_construction"
  ],
  "propertyType": "any",
  "minJobValue": 0,
  "requireContractor": false,
  "minLeadScore": 0,
  "maxLeads": 25,
  "monitorMode": false,
  "watchName": "default"
}
```

# Actor output Schema

## `leads` (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 = {
    "cities": [
        "chicago",
        "nyc",
        "los_angeles",
        "san_francisco",
        "austin",
        "seattle",
        "new_orleans"
    ],
    "withinDays": 7,
    "trades": [
        "roofing",
        "solar",
        "hvac",
        "new_construction"
    ],
    "maxLeads": 25
};

// Run the Actor and wait for it to finish
const run = await client.actor("nerolabs/building-permit-leads").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 = {
    "cities": [
        "chicago",
        "nyc",
        "los_angeles",
        "san_francisco",
        "austin",
        "seattle",
        "new_orleans",
    ],
    "withinDays": 7,
    "trades": [
        "roofing",
        "solar",
        "hvac",
        "new_construction",
    ],
    "maxLeads": 25,
}

# Run the Actor and wait for it to finish
run = client.actor("nerolabs/building-permit-leads").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 '{
  "cities": [
    "chicago",
    "nyc",
    "los_angeles",
    "san_francisco",
    "austin",
    "seattle",
    "new_orleans"
  ],
  "withinDays": 7,
  "trades": [
    "roofing",
    "solar",
    "hvac",
    "new_construction"
  ],
  "maxLeads": 25
}' |
apify call nerolabs/building-permit-leads --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nerolabs/building-permit-leads"
        }
    }
}
```

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/b3qebpE0k4Ir3nKOw/builds/3VtTzuP6hJDs7F974/openapi.json
