# City Building Permits Feed (new since last run) (`moment-data/building-permits-feed`) Actor

New building permits from official open-data portals of Chicago, NYC, Los Angeles, Seattle, San Francisco and Denver, in one normalized schema. Schedule it daily and get only permits you haven't seen.

- **URL**: https://apify.com/moment-data/building-permits-feed.md
- **Developed by:** [Luke Miller](https://apify.com/moment-data) (community)
- **Security:** Limited permissions
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $3.00 / 1,000 permit 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

## City Building Permits Feed: New Permits Since Your Last Run

Building permits from the **official open-data portals** of Chicago, New York City, Los Angeles, Seattle, San Francisco and Denver, in **one normalized schema**. Schedule it daily and each run delivers only the permits you have not seen yet.

### What you get

- **One schema across six cities.** Permit ID, type, project kind, status, description, address, ZIP (where published), neighborhood, filed and issued dates, valuation in USD, housing units (where published), latitude and longitude, and a link back to the source dataset.
- **New since last run.** The Actor remembers what it delivered for each *State key*, so a daily schedule only pays for new permits. A short overlap re-scan catches permits that cities post late.
- **Lag-aware.** Cities refresh on their own schedules (Los Angeles runs about a week behind). Every run reports the newest record date each city has posted (`dataThrough`), and the lookback window is measured from that date, so slow portals still return data.
- **Filters.** City, project kind (new-construction, alteration, demolition, other), minimum valuation, keywords (solar, roof, pool, ADU, ...) and ZIP codes.
- **Business names only.** Contractor names are included only where the city publishes them and they look like a business (Denver, NYC, Chicago). Owner and applicant personal names and phone numbers are never copied into the output (free-text descriptions are passed through as the city published them).

### Example uses

- Roofing, solar, HVAC and window sales teams watching new permits in their ZIP codes.
- Suppliers and subcontractors tracking new-construction permits and big-valuation jobs.
- Market analysts counting new permits and demolitions by city and week.
- AI agents asking "what new-construction permits over $1M were issued in Denver this week?" through the Apify API or MCP server.

### Quick start

Run with the defaults to see a small sample (about 50 permits). For a daily feed, create a Schedule with `mode: sinceLastRun`, your cities and filters, higher caps, and a unique `stateKey`.

### Input (main fields)

| Field | Meaning |
|---|---|
| `mode` | `window` (default: everything in the last N days of data) or `sinceLastRun` (only permits not delivered before; **set this for daily schedules**) |
| `lookbackDays` | First-run or window length, default 3 |
| `cities` | any of `chicago`, `nyc`, `los-angeles`, `seattle`, `san-francisco`, `denver` |
| `projectKinds` | `new-construction`, `alteration`, `demolition`, `other` |
| `minValuationUsd`, `keywords`, `zipCodes` | extra filters |
| `maxItems`, `maxItemsPerCity` | caps per run (also cap your spend). Defaults are small (50 / 10) so a quick test run is cheap; raise them for production |
| `includeRaw` | adds the original city record; this can contain personal names, so leave it off unless you need it |
| `socrataAppToken` | optional free token from the portals, to avoid throttling on shared IPs |
| `stateKey` | one key per saved search; each keeps its own history |

### Output (example)

See the *Output* tab or `sample-output.json`. Each record looks like:

```json
{
  "city": "Seattle", "state": "WA", "source": "seattle-building-permits",
  "permitId": "7169560-CN", "permitType": "Addition/Alteration", "permitSubtype": "Multifamily",
  "projectKind": "alteration", "status": "Issued",
  "description": "Construct tenant improvements to unit 4 in a multi-family building ...",
  "address": "4025 MIDVALE AVE N", "zip": "98103",
  "filedDate": "2026-10-28", "issuedDate": "2026-10-28", "valuationUsd": 30000,
  "latitude": 47.656, "longitude": -122.344
}
```

Every run also writes a summary to the `OUTPUT` record: per city counts, `dataThrough`, and any source errors. One city failing never blocks the others.

### Data sources and licenses

| City | Dataset | Terms |
|---|---|---|
| Chicago | Building Permits (data.cityofchicago.org) | [City of Chicago Data Terms of Use](https://www.chicago.gov/city/en/narr/foia/data_disclaimer.html) |
| New York City | DOB NOW: Build, Approved Permits (data.cityofnewyork.us) | [NYC Open Data Terms of Use](https://www.nyc.gov/html/datamine/html/data/terms.html) |
| Los Angeles | Building and Safety, Permits Issued from 2020 to Present (data.lacity.org) | City of Los Angeles open data |
| Seattle | Building Permits (data.seattle.gov) | Public Domain per the portal |
| San Francisco | Building Permits (data.sf.gov) | Open Data Commons PDDL 1.0 |
| Denver | Residential, Commercial and Demolition Construction Permits (Denver Open Data Catalog) | [Denver open license](https://denvergov.org/opendata) |

**Disclaimer (required by the City of Chicago terms for derivative applications):** This Actor provides data that has been modified for use from its original sources, including 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 here. The data is subject to change at any time and is used at your own risk.

This Actor is not affiliated with or endorsed by any city. Permit data is as published by each city; valuations are self-reported on applications and can be wrong. The `projectKind` field is a best-effort label from the permit type, not an official classification.

### Good-citizen behavior

Requests are spaced at least 0.4 seconds apart per host, use a descriptive User-Agent, back off on errors and rate limits, and only the changed window is requested.

### Pricing

Pay per event (Apify takes care of billing; you only pay for what is delivered):

| Event | Price | Charged |
|---|---|---|
| `apify-actor-start` | $0.005 | once per run |
| `permit-record` | $0.003 | per building permit delivered |

A default test run (50 permits) costs about $0.155. A daily `sinceLastRun` schedule for one city and a few ZIP codes (~20 new permits/day) costs about $0.065/day. `maxItems` and Apify's *max cost per run* cap your spend; the Actor stops delivering when your budget is reached, so you are never charged for more than you set. No separate platform-usage charges: compute is included in the event prices.

### FAQ

**Which cities are covered?** Chicago, New York City, Los Angeles, Seattle, San Francisco and Denver, from each city's official open-data portal. More cities may be added; tell us which ones you need in the Issues tab.

**Why are some cities a few days behind?** Each city refreshes its portal on its own schedule. The run summary (`OUTPUT`) shows the newest date each city has posted (`dataThrough`).

**Are homeowner names included?** No. Only business contractor names, where the city publishes them. No owner or applicant personal names or phone numbers.

**How do I get only new permits every day?** Create a Schedule with `mode: sinceLastRun`, your filters and a unique `stateKey`.

# Actor input Schema

## `mode` (type: `string`):

window (default): everything issued in the last N days of data, no saved state. sinceLastRun: only permits not delivered before for this State key; use this for daily schedules.

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

How far back the first run (or window mode) looks.

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

Which city portals to read. Default is all six.

## `projectKinds` (type: `array`):

Best-effort classification from the permit type labels. Empty = all.

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

Skip permits below this reported cost. Permits with no reported cost count as 0.

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

Keep permits whose type or description contains any of these words (e.g. solar, roof, pool, ADU).

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

Keep only these 5-digit ZIPs. Chicago and Denver records have no ZIP, so they are skipped when this filter is set.

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

Hard cap on delivered records across all cities (also caps your spend).

## `maxItemsPerCity` (type: `integer`):

Cap per upstream dataset. In sinceLastRun mode the rest is delivered on the next run.

## `includeRaw` (type: `boolean`):

Adds a 'raw' field with the original city record. Owner and applicant personal names stay in raw, so leave this off unless you need it.

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

Optional free token from the city portal to avoid shared-IP throttling.

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

Name of the saved search. Each key keeps its own 'already delivered' history, so use one key per schedule.

## Actor input object example

```json
{
  "mode": "window",
  "lookbackDays": 3,
  "cities": [
    "chicago",
    "nyc",
    "los-angeles",
    "seattle",
    "san-francisco",
    "denver"
  ],
  "minValuationUsd": 0,
  "maxItems": 50,
  "maxItemsPerCity": 10,
  "includeRaw": false,
  "stateKey": "default"
}
```

# Actor output Schema

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

One record per permit.

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

Counts per city, newest record date posted by each city, and any source errors.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("moment-data/building-permits-feed").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("moment-data/building-permits-feed").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 '{}' |
apify call moment-data/building-permits-feed --silent --output-dataset

```

## MCP server setup

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

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/hBj90cKVsdDDah52q/builds/aHFIaarkQdaCGx8CT/openapi.json
