# Building Permits Scraper: New Construction Leads by City (`fedsignals/building-permits-monitor`) Actor

Returns newly issued building permits from 22 official US open-data portals (NYC, LA, Chicago and more) in one schema: type, value, address, ZIP, coordinates, business contractor and trade tags (solar, roofing, HVAC...). Filter by trade, value and ZIP; only new permits.

- **URL**: https://apify.com/fedsignals/building-permits-monitor.md
- **Developed by:** [FedSignals Data](https://apify.com/fedsignals) (community)
- **Categories:** Real estate, Lead generation, Automation
- **Stats:** 2 total users, 1 monthly users, 0.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $4.00 / 1,000 permits

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

## Building Permits Scraper: New Construction Leads by City

A building permit shows a project is about to happen, often weeks before a contractor is hired or materials are ordered. This Actor reads newly **issued building permits** from 22 official open-data portals, biggest metros first: **New York City, Los Angeles, Chicago, San Antonio, San Jose, Austin, San Francisco, Columbus, Seattle, Washington DC, Boston, Raleigh, Minneapolis, Mesa, New Orleans, Pittsburgh, Cincinnati, Baton Rouge, Tempe, Cambridge MA, Montgomery County MD and New Jersey (statewide)**. It maps them into **one common schema** and tags each permit by trade (solar, roofing, HVAC, electrical, plumbing, EV charging, pool, ADU, demolition, new construction, and more). You can filter by trade, project value, property class, ZIP code or keyword. Incremental mode returns **only permits you haven't seen before**.

You don't need an API key. **No personal data is collected.** The Actor never reads owner or applicant names, contractor phone numbers or contractor addresses. For the newer portals it requests only the columns it maps, so those fields are never even downloaded. It returns a contractor name only when the name looks like a business, such as an LLC, Inc or a name containing "Roofing" or "Electric". A bare personal name, as sole proprietors often file under, is dropped.

### Example input

```json
{
  "cities": ["new-york", "los-angeles", "chicago", "austin", "columbus"],
  "daysBack": 7,
  "tradeTags": ["solar", "roofing", "hvac", "ev-charging"],
  "propertyClasses": ["Residential"],
  "minEstimatedCost": 5000,
  "onlyNew": true,
  "maxItems": 1000
}
```

### Example output

```json
{
  "city": "San Francisco, CA",
  "permitNumber": "S20260921748",
  "propertyClass": "Residential", "proposedUse": "2 family dwelling",
  "description": "solar permit: ... including 2 energy storage unit ... 13.5 kwh each ...",
  "status": "issued", "appliedDate": "2026-09-21", "issuedDate": "2026-10-19",
  "estimatedCost": 14000.0,
  "address": "738 El Camino Del Mar", "zip": "94121", "latitude": 37.7865, "longitude": -122.4913,
  "contractorCompany": null, "contractorTrade": null,
  "tradeTags": ["solar"],
  "url": "https://dbiweb02.sfgov.org/dbipts/default.aspx?page=Permit&PermitNumber=S20260921748",
  "sourceDataset": "https://data.sfgov.org/Housing-and-Buildings/Building-Permits/i98e-djp9",
  "licence": "ODC Public Domain Dedication and License (PDDL)"
}
```

Every row has the same fields: `city, cityKey, permitNumber, permitType, workClass, propertyClass, proposedUse, description, status, appliedDate, issuedDate, estimatedCost, address, zip, municipality, latitude, longitude, housingUnitsAdded, contractorCompany, contractorTrade, tradeTags, url, sourceDataset, licence, firstSeenAt`. A field is `null` when the city doesn't publish it. For example, Boston has no applied date, Washington DC publishes no project value, Chicago has no ZIP, and New Jersey's statewide feed has no street address (only the municipality). `municipality` is filled for New York City (borough), Montgomery County MD and New Jersey.

If you don't choose cities, the Actor uses the original five (San Francisco, Seattle, Austin, New Orleans, Boston) so existing schedules keep the same cost. Select the others in the `cities` field.

**Live test, Oct 29, 2026** (all 22 portals, permits issued Oct 22–29): **11,390 permit rows** in about 34 seconds, with every portal answering. Rows by portal: New York City 3,494, Austin 1,315, Minneapolis 1,077, Washington DC 746, Columbus 720, San Antonio 702, Chicago 598, Los Angeles 559, San Jose 457, New Orleans 457, San Francisco 303, Cincinnati 190, Baton Rouge 173, Pittsburgh 141, Seattle 111, Raleigh 111, Tempe 61, Montgomery County MD 61, Mesa 52, Boston 45, Cambridge 17, New Jersey 0 (the state feed lags about 10 days; 7,000+ rows a week appear once processed). The earlier 5-city test (Oct 28) returned 2,123 permits, and the home-energy filter above returned 91.

### Use cases

- **Home-services lead lists.** Roofers, solar and battery installers, HVAC, pool, fencing and remodel companies can find new projects (and competitors' jobs) by ZIP code.
- **Suppliers and distributors.** See where the work is: new construction, ADUs and commercial tenant improvements.
- **Real-estate investors and analysts.** Track new housing units, demolitions and renovation hot spots.
- **Insurance, solar-finance and proptech teams.** Feed permit activity into risk or propensity models.

### Data source and terms

All data comes from official open-data APIs: Socrata SODA, CKAN DataStore and ArcGIS FeatureServer query endpoints. Only datasets whose published licence or terms allow commercial reuse are included, and each row carries its `licence` and `sourceDataset`. Licences and terms were checked on each portal on Oct 28–29, 2026:

| City / area | Dataset | Licence or terms |
|---|---|---|
| New York City, NY | [rbx6-tga4](https://data.cityofnewyork.us/Housing-Development/DOB-NOW-Build-Approved-Permits/rbx6-tga4) | NYC Open Data: public data, no restrictions on use (NYC Open Data Law; Terms of Use: as-is) |
| Los Angeles, CA | [pi9x-tg5x](https://data.lacity.org/City-Infrastructure-Service-Requests/Building-and-Safety-Building-Permits-Issued-from/pi9x-tg5x) | City of Los Angeles Open Data Terms of Use (no reuse restriction; as-is disclaimer) |
| Chicago, IL | [ydr8-5enu](https://data.cityofchicago.org/Buildings/Building-Permits/ydr8-5enu) | City of Chicago Data Portal Terms of Use (reuse allowed; apps must show the City's disclaimer, see README) |
| San Antonio, TX | [c21106f9-3ef5-4f3a-8604-f992b4db7512](https://data.sanantonio.gov/dataset/building-permits) | Creative Commons Attribution (CC BY), City of San Antonio |
| San Jose, CA | [045b3678-e923-4002-b696-300955bc6d06](https://data.sanjoseca.gov/dataset/last-30-days-building-permits) | Creative Commons CCZero (CC0) |
| Austin, TX | [3syk-w9eu](https://data.austintexas.gov/Building-and-Development/Issued-Construction-Permits/3syk-w9eu) | Public Domain (U.S. Government Works) |
| San Francisco, CA | [i98e-djp9](https://data.sfgov.org/Housing-and-Buildings/Building-Permits/i98e-djp9) | ODC Public Domain Dedication and License (PDDL) |
| Columbus, OH | [FeatureServer layer](https://opendata.columbus.gov/datasets/building-permits) | Creative Commons CC0 (public domain), City of Columbus |
| Seattle, WA | [76t5-zqzr](https://data.seattle.gov/Built-Environment/Building-Permits/76t5-zqzr) | Public Domain |
| Washington, DC | [FeatureServer layer](https://opendata.dc.gov/search?q=building%20permits) | Creative Commons Attribution 4.0 (CC BY 4.0), District of Columbia |
| Boston, MA | [6ddcd912-32a0-43df-9908-63574f8c7e77](https://data.boston.gov/dataset/approved-building-permits) | ODC Public Domain Dedication and License (PDDL) |
| Raleigh, NC | [FeatureServer layer](https://data-ral.opendata.arcgis.com/datasets/building-permits) | Open Raleigh Data Policy (terms of use: as-is; City may ask users to stop redistribution) |
| Minneapolis, MN | [FeatureServer layer](https://opendata.minneapolismn.gov/search?q=CCS_Permits) | CC0-style waiver: City of Minneapolis has waived all copyright and related rights |
| Mesa, AZ | [dzpk-hxfb](https://citydata.mesaaz.gov/Development-Services/Building-Permits/dzpk-hxfb) | City of Mesa open license (Management Policy 124: no restrictions on copying, distribution, modification or commercial use) |
| New Orleans, LA | [72f9-bi28](https://data.nola.gov/Housing-Land-Use-and-Blight/Permits-BLDS/72f9-bi28) | CC0 1.0 Universal (Public Domain Dedication) |
| Pittsburgh, PA | [f4d1177a-f597-4c32-8cbf-7885f56253f6](https://data.wprdc.org/dataset/pli-permits) | Creative Commons Attribution (CC BY), City of Pittsburgh via WPRDC |
| Cincinnati, OH | [uhjb-xac9](https://data.cincinnati-oh.gov/Thriving-Neighborhoods/Cincinnati-Building-Permits/uhjb-xac9) | Public Domain |
| Baton Rouge, LA | [7fq7-8j7r](https://data.brla.gov/Housing-and-Development/EBR-Building-Permits/7fq7-8j7r) | Public Domain |
| Tempe, AZ | [FeatureServer layer](https://data.tempe.gov/search?q=permits%20issued%20by%20building%20safety) | Creative Commons Attribution 4.0 (CC BY 4.0), City of Tempe |
| Cambridge, MA | [qu2z-8suj](https://data.cambridgema.gov/Inspectional-Services/Building-Permits-Addition-Alteration/qu2z-8suj) | ODC Public Domain Dedication and License (PDDL) |
| Montgomery County, MD | [m88u-pqki](https://data.montgomerycountymd.gov/Consumer-Housing/Residential-Permit/m88u-pqki) | Public Domain |
| New Jersey (statewide) | [w9se-dmra](https://data.nj.gov/Reference-Data/NJ-Construction-Permit-Data/w9se-dmra) | Public Domain |

Sources with terms of use rather than a standard licence are labelled that way above (Los Angeles, New York City, Chicago, Raleigh); those cities may change their terms or ask users to stop.

**Attribution (CC BY sources):** contains data from the City of San Antonio, the City of Pittsburgh (via the Western Pennsylvania Regional Data Center), the District of Columbia (Open Data DC) and the City of Tempe, used under Creative Commons Attribution licences. The data has been reformatted into a common schema.

**City of Chicago disclaimer (required by its terms of use):** "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."

Left out on purpose: portals with no stated licence, a non-commercial or share-alike licence, or a feed that stopped updating (for example Dallas, Houston, Detroit, Charlotte, Miami-Dade and Louisville). Trade tags and property class are keyword-based, best-effort labels. Always check the permit on the city's site before acting on it. Requests are rate-limited, retried with backoff on errors, and identified with a contact User-Agent. If one portal is down, the run continues with the others and the error is recorded in the `SUMMARY` record. An optional free Socrata app token raises rate limits.

### Tips

- Add `excludeKeywords` such as `["revision", "extension"]` to drop paperwork-only permits.
- New York City and Austin are the largest feeds (over 1,000 a week each). Set `maxPerCity` if you only want a sample.
- New Jersey's statewide feed is processed monthly, so use `daysBack: 30` or more for it.
- New York City lists each work permit and its renewal sequence separately; incremental memory treats them as separate permits.

### Scheduling

Run it **daily** with `daysBack: 7` and `onlyNew: true`. Some cities post permits a few days late (Seattle's and New Orleans' feeds lagged 5–6 days in testing). The 7-day look-back catches late rows, and incremental memory keeps you from being charged twice. Use a separate `stateKey` for each saved task, for example one task per trade or territory, and connect the dataset to Google Sheets, a CRM or a webhook.

### How much does it cost?

Pay per event: you pay only for the events below plus Apify's $0.00005 run-start fee. Platform compute is included in the price.

- **$4 per 1,000 permits** ($0.004 per permit delivered).

Real examples from our test runs:

- **Quick-start input** (10 permits): 10 × $0.004 = **about $0.04**.
- **Every permit from the default five cities for a week**: 2,123 permits in our Oct 28 test = **about $8.49**.
- **Every permit from all 22 portals for a week**: 11,390 permit rows in our Oct 29 local test = **about $45.56**. Use trade, value and ZIP filters to cut this down.
- **Home-energy leads** (solar, roofing, HVAC, EV charging; residential; $5k+; 14 days): 91 permits in our test = **about $0.36**.

Use `maxItems` and Apify's per-run spending limit to cap spend; the Actor stops cleanly when the limit is reached.

### Limits

- Some city feeds post permits several days late; keep `daysBack` at 7.
- Trade tags and property class are keyword-based, best-effort labels.
- Redaction of free-text descriptions is pattern-based, so it is best effort.

### FAQ

**Which cities are covered?**\
22 portals: New York City, Los Angeles, Chicago, San Antonio, San Jose, Austin, San Francisco, Columbus, Seattle, Washington DC, Boston, Raleigh, Minneapolis, Mesa, New Orleans, Pittsburgh, Cincinnati, Baton Rouge, Tempe, Cambridge MA, Montgomery County MD and New Jersey (statewide). See the licence table under Data source and terms.

**Can you add my city?**\
Open an issue with the city's open-data portal link. Cities with an open licence (public domain, PDDL, CC0 or similar) can be added.

**Does it return homeowner names or phone numbers?**\
No. Owner and applicant names, phones and contractor addresses are never read, and emails and phone numbers in descriptions are redacted.

**Why is the value or contractor empty?**\
Not every city publishes every field. A field is null when the portal doesn't provide it.

### Reliability

Measured on Apify as of Oct 29, 2026: 4 of 4 of our own test runs succeeded (100%), with a median run time of 4 seconds (longest 7 s) on small test inputs. It is a small sample from a new Actor; we will update it as runs accumulate. Local test, Oct 29, 2026: 7 days from all 22 portals returned 11,390 permit rows in about 34 seconds with every portal answering; one failing portal never fails the run.

### Use it from AI agents (MCP)

Add this Actor as a tool in Claude Desktop, Cursor or any MCP client through the Apify MCP server:

```json
{
  "mcpServers": {
    "building-permits-monitor": {
      "url": "https://mcp.apify.com?tools=fedsignals/building-permits-monitor",
      "headers": { "Authorization": "Bearer YOUR_APIFY_TOKEN" }
    }
  }
}
```

### Use it from code (API)

One call runs the Actor and returns the dataset items. `maxTotalChargeUsd` caps what the run can charge.

Python:

```python
import requests

items = requests.post(
    "https://api.apify.com/v2/acts/fedsignals~building-permits-monitor/run-sync-get-dataset-items",
    params={"maxTotalChargeUsd": 1},
    headers={"Authorization": "Bearer YOUR_APIFY_TOKEN"},
    json={"cities": ["san-francisco", "austin"], "daysBack": 7, "onlyNew": False, "maxItems": 10},
    timeout=300,
).json()
print(len(items), "items")
```

JavaScript (Node 18+):

```js
const res = await fetch(
  "https://api.apify.com/v2/acts/fedsignals~building-permits-monitor/run-sync-get-dataset-items?maxTotalChargeUsd=1",
  {
    method: "POST",
    headers: { Authorization: "Bearer YOUR_APIFY_TOKEN", "Content-Type": "application/json" },
    body: JSON.stringify({"cities": ["san-francisco", "austin"], "daysBack": 7, "onlyNew": false, "maxItems": 10}),
  }
);
const items = await res.json();
console.log(items.length, "items");
```

### Related FedSignals Actors

- [SAM.gov Scraper: Federal Contract Opportunities Monitor](https://apify.com/fedsignals/federal-opportunities-monitor): new SAM.gov contract opportunities, filtered and deduplicated
- [SEC Form D Scraper: New Startup Funding Rounds](https://apify.com/fedsignals/sec-form-d-monitor): new private funding rounds from SEC Form D filings
- [Expiring Federal Contracts: Recompete Finder (USAspending)](https://apify.com/fedsignals/federal-recompete-finder): federal contracts expiring soon, with incumbents
- [USAspending Scraper: Federal Spending Trends by Category](https://apify.com/fedsignals/federal-spending-signals): where federal contract spending is growing

### Feedback

Found a bug or need a filter or source? Open an issue on the Issues tab or email **fedsignals.alerts@gmail.com**.

This Actor is not affiliated with or endorsed by any city government.

# Actor input Schema

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

Returns permits from these portals (biggest metros first). Only datasets whose licence or terms allow commercial reuse are offered. Empty = the original five (San Francisco, Seattle, Austin, New Orleans, Boston).

## `daysBack` (type: `integer`):

Returns permits issued on or after today minus N days. Use 7 with a daily schedule so late-posted permits are caught.

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

Returns only permits tagged with any of these trades (from permit type and description keywords). Empty = all.

## `propertyClasses` (type: `array`):

Residential, Mixed or Commercial (best-effort label from the city's class, use or occupancy field).

## `minEstimatedCost` (type: `integer`):

Returns only permits with a declared value of at least this many USD. Permits without a value are skipped when set.

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

Only these 5-digit ZIP codes.

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

Match permit type, work class, description or use.

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

Drop permits mentioning any of these.

## `onlyNew` (type: `boolean`):

On: returns only permits not delivered by an earlier run with the same filters. Turn on for scheduled runs.

## `stateKey` (type: `string`):

Optional. Separate incremental memory per saved task. Defaults to a hash of your filters.

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

Maximum permits returned (and charged) per run.

## `maxPerCity` (type: `integer`):

Safety cap on rows read from each portal per run.

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

Optional free Socrata app token for higher rate limits on the Socrata portals.

## `minRequestIntervalSec` (type: `number`):

Polite delay between API calls.

## Actor input object example

```json
{
  "cities": [
    "san-francisco",
    "austin"
  ],
  "daysBack": 7,
  "tradeTags": [],
  "propertyClasses": [],
  "zipCodes": [],
  "keywords": [],
  "excludeKeywords": [],
  "onlyNew": true,
  "maxItems": 10,
  "maxPerCity": 5000,
  "minRequestIntervalSec": 0.5
}
```

# Actor output Schema

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

Newly issued building permits in one schema. One item per row; field definitions are in the dataset schema.

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

JSON summary of the run (counts scanned, pushed and skipped).

# 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": [
        "san-francisco",
        "austin"
    ],
    "daysBack": 7,
    "onlyNew": false,
    "maxItems": 10
};

// Run the Actor and wait for it to finish
const run = await client.actor("fedsignals/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 = {
    "cities": [
        "san-francisco",
        "austin",
    ],
    "daysBack": 7,
    "onlyNew": False,
    "maxItems": 10,
}

# Run the Actor and wait for it to finish
run = client.actor("fedsignals/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 '{
  "cities": [
    "san-francisco",
    "austin"
  ],
  "daysBack": 7,
  "onlyNew": false,
  "maxItems": 10
}' |
apify call fedsignals/building-permits-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,fedsignals/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/ar8voWTs20ZpWaidu/builds/GcVCx5aPSyLgnoiub/openapi.json
