# US Building Permits Monitor: Daily Construction Leads by Trade (`optimistprime/us-building-permits-monitor`) Actor

New building permits as roofing, solar, HVAC and construction leads from NYC, LA, SF, Seattle, Austin and 4 more official city and county portals. Filter by trade, value, ZIP. New-only monitor mode. $1.50/1k. Not affiliated with or endorsed by any government agency.

- **URL**: https://apify.com/optimistprime/us-building-permits-monitor.md
- **Developed by:** [Derek VanLaningham](https://apify.com/optimistprime) (community)
- **Categories:** Lead generation, Real estate, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $1.20 / 1,000 building permit returneds

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

## US Building Permits Monitor: Daily Construction Leads by Trade

New US building permits every day, from **9 official city and county open-data portals**, filtered by trade, value and ZIP. Monitor mode, no API key, no contact scraping, **$1.50 per 1,000**.

**Not affiliated with or endorsed by any government agency.**

This Actor is a **building permits scraper / construction leads API** built on official open data (not city website HTML scraping). Trade tags are **approximate** keyword heuristics — always verify against `permitType`, `workClass` and `description`.

### What you get

One normalized row per permit:

| Field | Notes |
|---|---|
| `permitKey` | `{source}:{permitNumber}` |
| `jurisdiction` | e.g. New York City, NY |
| `permitType` / `workClass` / `status` | As published |
| `tradeTags` | Approximate: solar, roofing, hvac, electrical, plumbing, ev-charger, pool, demolition, new-construction, adu, addition-alteration, signage, windows-doors |
| `isNewConstruction` | `true` when `new-construction` ∈ tradeTags |
| `description` | Phones/emails scrubbed; max 2,000 chars |
| `valuationUsd` | Declared/estimated value (null if missing; Austin has none) |
| `issuedDate` / `appliedDate` | YYYY-MM-DD |
| `address` | Site address of the permitted work (property, not a person) |
| `contractors` | **Business names only** (legal suffix / trade-word filter) |
| `ownerBusiness` | Owner **only if a business** (never a person name) |
| `sourceUrl` + `licence` | Citation |
| `changeType` | `new` / `updated` in monitor mode |

### Why this Actor

- Official open-data APIs only (Socrata SODA 2.1) — no logins, CAPTCHAs or HTML scraping
- One schema across 9 areas
- Trade tags for roofing, solar, HVAC, pool and more (**approximate**)
- Valuation and ZIP filters
- Monitor mode (new + changed only)
- Per-source freshness in `RUN_SUMMARY`
- Privacy-safe (no owner names or phones — competitors sell contacts; we do not)
- MCP-ready

### Coverage

| Source | Dataset | Typical weekly volume | Valuation | Contractors | Lag |
|---|---|---|---:|---|---|
| NYC | [rbx6-tga4](https://data.cityofnewyork.us/Housing-Development/DOB-NOW-Build-Approved-Permits/rbx6-tga4) | ~3,700 | yes | business applicant | ~3 days |
| Los Angeles | [pi9x-tg5x](https://data.lacity.org/City-Infrastructure-Service-Requests/Building-Permits/pi9x-tg5x) | ~1,400 | yes | — | ~1–2 days |
| San Francisco | [i98e-djp9](https://data.sfgov.org/Housing-and-Buildings/Building-Permits/i98e-djp9) | ~250 | yes | — | ~1–2 days |
| Seattle | [76t5-zqzr](https://data.seattle.gov/) | ~125 | yes | — | low |
| Austin | [3syk-w9eu](https://data.austintexas.gov/) | ~1,100 | **none** | company name | low |
| Cincinnati | [uhjb-xac9](https://data.cincinnati-oh.gov/) | ~230 | yes | company | low |
| Baton Rouge | [7fq7-8j7r](https://data.brla.gov/) | ~160 | yes | company | low |
| New Orleans | [rcm3-fn58](https://data.nola.gov/) | ~320 | yes | list | low |
| Montgomery County MD | commercial + residential | ~180 | yes | — | low |

**Chicago is not included in v1** (portal terms: indemnity, mandatory disclaimer, stop-right).

**Known limits:** trade tags are approximate; LA `solar` flags are sparse; Cincinnati descriptions are coarse; NYC Mechanical Systems filings often tag as `addition-alteration` rather than `hvac`.

### How to use

#### Daily roofing / solar / HVAC leads (monitor + schedule)

1. Set `tradeTags` (e.g. `["roofing"]`), `monitorMode: true`, pick a `monitorId`.
2. Schedule daily. Webhook the dataset to Slack, email, Make, n8n or Zapier.
3. Only **new** (and optionally **updated**) permits are returned and charged.

```json
{ "tradeTags": ["roofing"], "monitorMode": true, "monitorId": "roofing-daily", "firstRunLookbackDays": 3, "maxItems": 200 }
```

- **First run:** loads the last `firstRunLookbackDays` days (default **3**), not a big backlog. Set it higher (up to 30) if you want more history on day one.
- **Later runs:** only permits that are new since the previous run. `lookbackDays` (default 14) re-checks recent days for late-published permits, but never reaches back before your first run.
- **Capped runs mix cities:** when `maxItems` cuts a run short, rows are taken round-robin across cities (newest first within each city), so a 50-row all-city run returns a mix instead of only the city with the latest dates. Uncapped runs stay newest-first.
- **Capped runs resume:** if a run stops at `maxItems` or your spend limit, the status says "Returned X of Y+ new permits. Raise maxItems or your spend limit." The next run continues with the next permit not yet returned, so nothing is skipped and nothing you already received is charged again. Y is a lower bound when it ends in "+".
- **Approximate cost:** all 9 cities, no filters, is a few hundred permits per weekday; a trade filter like roofing is typically tens per day. At $0.0015 per permit, 100 permits ≈ $0.15 plus the $0.002 start fee.

#### Large projects over $1M

```json
{ "minValuationUsd": 1000000, "issuedWithinDays": 7, "maxItems": 500 }
```

Austin is skipped with a warning (no valuation column).

#### New construction in your ZIPs

```json
{ "newConstructionOnly": true, "zipCodes": ["78702", "78757"], "issuedWithinDays": 30 }
```

### Input highlights

| Input | Default | Notes |
|---|---|---|
| `sources` | all 9 | NYC, LA, SF, Seattle, Austin, Cincinnati, Baton Rouge, New Orleans, Montgomery MD |
| `issuedWithinDays` | 7 | 1–90 |
| `tradeTags` | — | Approximate keyword tags |
| `minValuationUsd` | — | Excludes null valuations; skips Austin |
| `zipCodes` | — | 5-digit |
| `monitorMode` | false | New (+ updates) only |
| `lookbackDays` | 14 | Covers NYC lag (never before the monitor's first run) |
| `firstRunLookbackDays` | 3 | Days loaded on a monitor's first run |
| `maxItems` | 50 | 0 = no limit |
| `timeBudgetSeconds` | 120 | Partial results with `truncated=true` |
| `socrataAppToken` | — | Optional secret; raises Socrata rate limits for very large jobs |

### Pricing

| Event | FREE | BRONZE | SILVER+ |
|---|---:|---:|---:|
| `permit` (primary) | $0.0015 | $0.0014 | $0.0012 |
| Actor start | $0.002 | $0.002 | $0.002 |

Minimum max total charge per run: **$0.02**. Platform memory start fee: at the **512 MB** default you pay one start event ($0.002). Raising memory above 1 GB charges $0.002 per GB.

**Examples (FREE):** default 50 rows ≈ **$0.077**; weekly solar (~213) ≈ **$0.32**; weekly ≥$1M (~303) ≈ **$0.46**.

### Use as an MCP tool

Endpoint: `https://mcp.apify.com/?tools=optimistprime/us-building-permits-monitor`

Example agent call:

```json
{ "sources": ["austin"], "tradeTags": ["pool"], "issuedWithinDays": 7, "maxItems": 10 }
```

Prompts:

- "Find new roofing permits issued in Los Angeles this week with a declared value over $20,000."
- "Which contractors pulled permits for projects over $1M in NYC in the last 7 days?"
- "List new pool permits in Austin ZIP 78757 this month."

### Privacy

We **never** fetch or output owner, applicant or contractor **person** names, phones, licence numbers, emails or contractor home addresses. Business names are kept only when they pass a business-marker filter (LLC, Inc, Construction, Roofing, Electric, …). Site **addresses** are kept — they identify the property being permitted.

### Data sources & licences

Official city/county open-data portals (public domain, PDDL, CC0 or NYC Open Data Law). Attribution is carried on every row in `licence`.

If you are a data owner and want a dataset removed, open an issue.

### FAQ

**Why no homeowner names or phone numbers?** Privacy. You get the site address and contractor **company** (when published and business-marked). We do not sell contact data.

**Why is Austin missing valuation?** The Issued Construction Permits dataset has no valuation column.

**How fresh is NYC?** Typically ~3 days behind the calendar date.

**Can you add my city?** Candidates for v1.1 include Chicago (if terms allow), Marin, Cambridge, Norfolk, Calgary, Edmonton and NJ open-data portals.

**Is this a scraper?** It reads official open-data APIs (SODA). No city website HTML scraping, no CAPTCHA bypass. Store search may still call it a building permit scraper / construction leads tool.

### Ready-to-run examples

Each example is a **published task**: open its page and click **Try**, or paste the JSON into Input.

| Example | Task | Sample run | Rows |
|---|---|---|---:|
| New solar permits (weekly) | [new-solar-permits-weekly](https://apify.com/optimistprime/us-building-permits-monitor/examples/new-solar-permits-weekly) | `2k47b8ERNNIwAodCu` | 175 |
| New roofing permits (weekly) | [new-roofing-permits-weekly](https://apify.com/optimistprime/us-building-permits-monitor/examples/new-roofing-permits-weekly) | `gBPdmVCp8U2VzvJzt` | 883 |
| Construction permits over $1M (weekly) | [construction-permits-over-1m-weekly](https://apify.com/optimistprime/us-building-permits-monitor/examples/construction-permits-over-1m-weekly) | `dBqlIFEfbASnueDXK` | 199 |

Times and row counts from real runs on Oct 5, 2026 (FREE-tier prices); results change as city portals update.

### Changelog

See [CHANGELOG.md](CHANGELOG.md).

# Changelog

This Actor's version history is a separate document: https://apify.com/optimistprime/us-building-permits-monitor/changelog.md

# Actor input Schema

## `sources` (type: `array`):

Leave empty for all 9 (Chicago excluded from v1). Official open-data portals only.

## `issuedWithinDays` (type: `integer`):

Only return permits with issuedDate on or after today minus N days (America/Chicago). In monitor mode the first run uses 'First monitor run: days back' instead.

## `tradeTags` (type: `array`):

Match any. Tags are approximate keyword heuristics; see README.

## `newConstructionOnly` (type: `boolean`):

Keep only permits tagged new-construction (approximate keyword tag).

## `minValuationUsd` (type: `integer`):

Permits without a reported value are excluded when set. Austin publishes no valuation and is skipped (with a warning).

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

5-digit ZIPs. Empty = all.

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

Matched case-insensitively in permit type, work class and description.

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

Drop rows whose type, work class or description contains any of these (case-insensitive).

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

Return only permits not seen since the last run for this monitorId (plus updates if enabled).

## `monitorId` (type: `string`):

Namespace for monitor state in the building-permits-monitor KV store.

## `includeUpdates` (type: `boolean`):

In monitor mode, also return permits whose status, valuation, type, work class or contractors changed.

## `lookbackDays` (type: `integer`):

Monitor re-runs re-check this many days so late-published permits are caught (NYC publishes ~3 days behind). Never reaches back before the monitor's first run.

## `firstRunLookbackDays` (type: `integer`):

Monitor mode only. How many days of permits the first run of a new monitorId loads (default 3). Later runs return only permits new since the previous run.

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

0 = no limit. Prefilled 11 so a first try costs under $0.02.

## `timeBudgetSeconds` (type: `integer`):

0 = no limit. Partial results are returned with truncated=true.

## `socrataAppToken` (type: `string`):

Optional. Raises Socrata rate limits if you run very large jobs. Not needed for normal use.

## Actor input object example

```json
{
  "sources": [
    "nyc",
    "la"
  ],
  "issuedWithinDays": 7,
  "newConstructionOnly": false,
  "monitorMode": false,
  "monitorId": "default",
  "includeUpdates": true,
  "lookbackDays": 14,
  "firstRunLookbackDays": 3,
  "maxItems": 11,
  "timeBudgetSeconds": 120
}
```

# Actor output Schema

## `permits` (type: `string`):

Dataset of building-permit records (recordType=building-permit).

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

Per-source freshness, counts, truncated/stopReason and charged events (KV RUN_SUMMARY).

# 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 = {
    "sources": [
        "nyc",
        "la"
    ],
    "maxItems": 11
};

// Run the Actor and wait for it to finish
const run = await client.actor("optimistprime/us-building-permits-monitor").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 = {
    "sources": [
        "nyc",
        "la",
    ],
    "maxItems": 11,
}

# Run the Actor and wait for it to finish
run = client.actor("optimistprime/us-building-permits-monitor").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 '{
  "sources": [
    "nyc",
    "la"
  ],
  "maxItems": 11
}' |
apify call optimistprime/us-building-permits-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,optimistprime/us-building-permits-monitor"
        }
    }
}
```

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/2cnUBBxdPlhwwCmfO/builds/LgpaQOgc9msHI1xdQ/openapi.json
