# US Building Permits – NYC, Chicago, SF, LA, Austin, Seattle (`liora-tools/us-building-permits`) Actor

Reliable permit feed: never a silent empty result. A city portal that is down gives a clear free error row; no match gives a free no_results row with the city's newest date. Permits from NYC, Chicago, SF, LA, Austin, Seattle in one schema: type, cost, address, contractor.

- **URL**: https://apify.com/liora-tools/us-building-permits.md
- **Developed by:** [Liora Blyth](https://apify.com/liora-tools) (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 $2.00 / 1,000 building 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

## US Building Permits API – NYC, Chicago, SF, LA, Austin, Seattle

Fresh **building permits from six major US cities** – New York City, Chicago, San Francisco, Los Angeles, Austin and Seattle – in one consistent schema: permit number, type, work description, status, issue date, estimated cost, address, ZIP, lat/long and contractor company. Built for construction & market activity monitoring and trend tracking: permit volume and estimated value by city, ZIP area and permit type, plus contractor company activity.

> Output is intended for market analysis; owner names / residential addresses where present in source data should not be used for personal outreach.

- ✅ **Never a silent empty dataset:** if a source is down you get a free `status: "error"` row naming it; if nothing matched you get a free `status: "no_results"`-type row saying so. Errors and status rows are never charged.
- ✅ Official open-data APIs only – no scraping of websites, no login, no proxies needed
- ✅ One clean, documented schema across all sources
- ✅ Pay only for results: **$0.002 per permit** ($2.00 per 1,000). Errors are free.

### Coverage

- **Cities / sources (Socrata open-data portals):** New York City (data.cityofnewyork.us, DOB permits), Chicago (data.cityofchicago.org), San Francisco (data.sf.gov), Los Angeles (data.lacity.org), Austin (data.austintexas.gov), Seattle (data.seattle.gov). Other cities are not covered.
- **Fields:** city, state, permit number, job number, type/subtype, work description, status, issued/applied date, estimated cost, address, borough, ZIP, lat/long, contractors, owner business, housing units, parcel id, permit URL, source dataset URL, licence, fetchedAt. A field the city does not publish stays `null` (fill rates per field in `RUN_SUMMARY`).
- **Freshness:** whatever each city has published; some cities lag days or weeks (see below).

### When a source is down – error, not empty

- Each city is fetched independently, with retries on HTTP 429 (honouring `Retry-After`).
- If a city portal still fails: **one free row** `{"city": "Chicago", "status": "error", "error": "…"}`; the other cities continue and the run ends **SUCCEEDED**.
- If the portals answered but no permit matched: **one free row** `{"status": "no_results", …}` plus, in `RUN_SUMMARY.cities.<city>.newestIssueDateInSource`, the newest permit date the city has published – so "the city has not published yet" is visible, not a silent empty dataset.
- With `onlyNewSinceLastRun` and nothing new: one free `status: "no_new"` row.

### Cost example and cost cap

- 6 cities × 200 permits (default) = up to 1,200 permits = **$2.40**. One city, 30 days, `minEstimatedCost: 100000` is typically 50–300 permits ($0.10–0.60).
- **Cap it two ways:** `maxItems` (total permits across all cities, e.g. `250` → max $0.50) and/or **Max charge per run** (Console → Run options, or `maxTotalChargeUsd` in the API). The Actor stops cleanly at the limit. Status rows are free.

### What can you use it for?

- Construction activity monitoring: daily permit volume and estimated value by city, ZIP area and permit type
- Trend tracking: how new-build, remodel, HVAC, EV charger and ADU permitting changes over time
- Contractor company activity: which firms are active, in which areas and at what project sizes
- Real-estate / investment research: where large projects are being permitted

### Input

| Field | What it does |
|---|---|
| `cities` | `nyc`, `chicago`, `san_francisco`, `los_angeles`, `austin`, `seattle` (default all) |
| `daysBack / issuedFrom / issuedTo` | Issue-date window |
| `keywords` | Matched in the work description, e.g. `new building`, `HVAC`, `ADU` |
| `minEstimatedCost` | Minimum job value in USD |
| `zipCodes` | Limit to ZIP codes |
| `permitTypeContains` | e.g. `electrical`, `new building` |
| `onlyWithContractor` | Only permits with a contractor company |
| `maxItemsPerCity` | Billable results cap per city |
| `maxItems` | Optional total billable cap for the whole run (empty = no total cap) |
| `socrataAppToken` | Optional free Socrata app token for heavy use |
| `onlyNewSinceLastRun` | For daily schedules: skip permits an earlier run already returned (not charged) |

Example input:

```json
{
  "keywords": [
    "new building",
    "HVAC"
  ],
  "daysBack": 30,
  "maxItemsPerCity": 15
}
```

### Output

Each result is one row in the dataset (download as JSON, CSV, Excel or via API). Example:

```json
{
  "city": "New York City",
  "state": "NY",
  "permitNumber": "M00998301-I1-GC",
  "jobNumber": "M00998301-I1",
  "permitType": "General Construction",
  "permitSubtype": "Renewal Permit Without Changes",
  "description": "FACADE AND ROOF REPAIRS AS PER PLANS FILED HEREWITH. NO CHANGE TO USE, EGRESS OR OCCUPANCY.",
  "status": "Permit Issued",
  "issuedDate": "2026-10-22",
  "appliedDate": "2024-04-08",
  "estimatedCost": 822799.5,
  "address": "32 WEST   40 STREET",
  "borough": "Manhattan",
  "zipCode": "10018",
  "latitude": 40.752747,
  "longitude": -73.983148,
  "contractors": [
    {
      "company": "NEW AMSTERDAM RESTORATION",
      "role": "Applicant (GC)",
      "licenseNumber": "622709"
    }
  ],
  "ownerBusiness": "MAXWELL-KATES, INC.",
  "housingUnits": null,
  "parcelId": "1008410069",
  "permitUrl": null,
  "source": "https://data.cityofnewyork.us/d/rbx6-tga4",
  "license": "NYC Open Data terms",
  "fetchedAt": "2026-10-24T16:45:07+00:00"
}
```

### Reliability – freshness and empty fields

- **Freshness differs by city.** Each city publishes on its own schedule (some daily, some with long delays). `RUN_SUMMARY` reports per city the newest issue date saved and, when nothing matched, the newest issue date available in the source – so you can tell "no new permits" from "the city has not published yet".
- **Field fill rates.** `RUN_SUMMARY.cities.<city>.fieldFillRate` shows the share of saved rows with permit number, issue date, type, description, address, ZIP, cost, contractors and coordinates. Some sources simply do not publish a field (e.g. Chicago's permit dataset has no site ZIP column), and those stay `null` rather than being filled with placeholders.
- **`onlyNewSinceLastRun`** remembers returned permit ids per filter combination in a named key-value store on your account; repeats are skipped and not charged. If nothing is new you get one free `status: "no_new"` row.
- Rate limits (HTTP 429) are retried with `Retry-After`; a city that fails gives a free `status: "error"` row while the other cities continue.
- No contact enrichment: rows contain only what the city publishes.

### Pricing

Pay-per-event, no subscription:

| Event | Price |
|---|---|
| Building permit (`permit`) | $0.002 ($2.00 / 1,000) |
| Actor start | $0.00005 |

Error rows and empty results are never charged. Set **Max charge per run** in Apify Console to cap your spend – the Actor stops cleanly when the limit is reached.

### Option names (shared across our open-data Actors)

The five Actors in this pack (Prediction Markets, US Building Permits, EU Company Registry, UK Public Tenders, EU Fuel Prices) use the same names for the same job:

| Job | Option | Notes |
|---|---|---|
| Free-text filter | `keywords` (array) | EU Company Registry uses `query` (one company name) + `registrationNumbers` |
| Date window | `daysBack` + `…From` / `…To` | `issuedFrom/To` (permits), `publishedFrom/To` (tenders) |
| Total billable cap | `maxItems` | available in all five; per-source caps (`maxMarketsPerPlatform`, `maxItemsPerCity`, `maxResultsPerCountry`, `maxPagesPerSource`) still work |
| Only new rows on schedules | `onlyNewSinceLastRun` | EU Fuel Prices: `onlyChangedSinceLastRun` (prices that changed) |
| Which sources | `platforms` / `cities` / `countries` / `sources` | the natural unit of each source |

Status rows always use the same `status` values: `error` / `source_unavailable` (source down), `no_results` / `no_match` / `not_found` (source answered, nothing matched), `no_new` (nothing new since last run). They are never charged.

### FAQ

**Where does the data come from?**\
The cities' official open-data portals (Socrata SODA API): NYC Open Data, Chicago Data Portal, DataSF, Los Angeles Open Data, Austin Open Data and Seattle Open Data. Public records, no login.

**Do you return homeowner names or phone numbers?**\
No. Person names and phone numbers are dropped on purpose; we keep contractor / business names and license numbers only. Sole-proprietor business names that are just a person's name are also removed.

**Why is estimated cost empty for some permits?**\
Some cities (e.g. Austin for trade permits) do not publish a job value for every permit type.

**How fresh is it?**\
Cities update daily or a few times a week; each run queries the portal live.

**Can you add my city?**\
Yes – any Socrata or ArcGIS permit dataset can be added. Open an issue on the Actor page.

### Legal & data sources

This Actor only reads public data published by government bodies or by the platforms' own public APIs under open licences (each row has `source`/`license` or `url` fields where available). It returns organisation-level data and deliberately excludes personal data. You are responsible for using the data in line with the source licence and applicable law (e.g. GDPR).

### Integrations

Schedule runs, then push results to Google Sheets, Slack, webhooks, Zapier, Make or your database with Apify integrations, or call the Actor from the Apify API / Python / JavaScript clients.

# Actor input Schema

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

City open-data portals to query.

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

Ignored when 'Issued from' is set.

## `issuedFrom` (type: `string`):

Optional start date.

## `issuedTo` (type: `string`):

Optional end date.

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

Match ANY of these in the work description, e.g. 'new building', 'HVAC', 'pool', 'demolition', 'ADU'.

## `permitTypeContains` (type: `array`):

Optional, e.g. 'electrical', 'new construction', 'alteration'.

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

Drop permits with a lower declared job value.

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

Optional ZIP filter.

## `onlyWithContractor` (type: `boolean`):

Available for NYC, Chicago, Austin, Seattle.

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

Newest first.

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

Optional total cap on billable permits for the whole run (same option name as in our other open-data Actors). Leave empty for no total cap; `maxItemsPerCity` still applies. For a hard dollar cap use 'Max charge per run' in Run options.

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

Optional free app token for higher rate limits on very large pulls. Not needed for normal use.

## `onlyNewSinceLastRun` (type: `boolean`):

For scheduled feeds: remembers permits already returned for the same cities and filters (in a named key-value store on your account) and skips them, so overlapping date windows are not charged twice.

## Actor input object example

```json
{
  "cities": [
    "nyc",
    "chicago",
    "san_francisco",
    "los_angeles",
    "austin",
    "seattle"
  ],
  "daysBack": 30,
  "keywords": [
    "new building",
    "HVAC"
  ],
  "onlyWithContractor": false,
  "maxItemsPerCity": 200,
  "onlyNewSinceLastRun": false
}
```

# Actor output Schema

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

Rows saved by this run (overview table).

# 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 = {
    "keywords": [
        "new building",
        "HVAC"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("liora-tools/us-building-permits").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 = { "keywords": [
        "new building",
        "HVAC",
    ] }

# Run the Actor and wait for it to finish
run = client.actor("liora-tools/us-building-permits").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 '{
  "keywords": [
    "new building",
    "HVAC"
  ]
}' |
apify call liora-tools/us-building-permits --silent --output-dataset

```

## MCP server setup

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

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/PKKBoZEiRysd6AJBj/builds/cltv1DHWYwm0tUHs4/openapi.json
