# US Building Permits Watch (`mikee368/us-building-permits-watch`) Actor

Search or watch building permits from New York, Chicago, Los Angeles, Austin, Seattle, San Francisco, Cincinnati, and Baton Rouge. No API key. No start fee.

- **URL**: https://apify.com/mikee368/us-building-permits-watch.md
- **Developed by:** [Mike Berckmoes](https://apify.com/mikee368) (community)
- **Categories:** Business
- **Stats:** 2 total users, 1 monthly users, 94.7% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$1.50 / 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

## US Building Permits Watch

**Search building permits across eight US cities, or return only the permits issued since your last run.** Categories and trade tags are normalised, so one input can ask for solar permits in Austin, Los Angeles and San Francisco. No API key is required.

This Actor is not affiliated with or endorsed by any of the cities below.

### What you get

| Field | Meaning |
|---|---|
| `city`, `state`, `permitNumber` | City and the permit id (New York is `job filing` plus `work permit`) |
| `permitTypeRaw`, `permitCategory`, `tags` | City type, one normalised category, and trade tags (`solar`, `roof`, `hvac`, `pool`, `adu`, `ev`) |
| `workDescription`, `status` | Description and status as published |
| `appliedDate`, `issuedDate`, `valuationUsd` | Dates (`YYYY-MM-DD`) and the city's job-value field |
| `address`, `zip`, `lat`, `lon`, `parcel` | Site location and parcel id when the city publishes them |
| `contractorCompany`, `contractorLicense`, `contractorPhone` | Contractor details when the city publishes them |
| `ownerName`, `applicantName` | Only if you turn on **Include owner and applicant names** |
| `sourceDataset`, `sourceUrl`, `retrievedAt` | Portal, record link, and the time the row was read |

Categories: `new_construction`, `addition_alteration`, `demolition`, `electrical`, `mechanical`, `plumbing`, `roofing`, `solar`, `pool`, `adu`, `ev_charger`, `other`.

### How to use it

- **Search.** Set **Issued from** and **Issued to**. The run returns matching permits and does not move the watch cursor.
- **Watch.** Leave both dates empty and schedule the Actor (for example every weekday morning). The first run looks back 3 days, or 14 days for Los Angeles, because LA's feed lags the calendar. Later runs return permits issued after the saved cursor. The named key-value store `permits-watch-state` keeps, per city, only the last issued-date cursor and the permit ids from that day.
- **Filters.** City, normalised category, keywords, and minimum valuation. Valuation fields mean different things in each city; do not rank cities against each other by `valuationUsd`.
- **Charge limit.** If a run hits the buyer's maximum charge, permits that were not stored are not marked as seen. The next run can deliver them. A permit is charged at most once per successful push.

A city portal that fails (after retries on HTTP 429 and 5xx) is reported as `source_unavailable` for that city. The other cities still finish, and the failed city's cursor stays where it was.

### How much does it cost?

Pay per permit: **$0.0015** for each `permit` event, and **no start fee**. A permit that is not stored is not charged. A weekday watch of a few hundred new permits is well under a dollar. Runs use 128–256 MB and do not use proxies.

Each request asks Socrata only for the columns this Actor maps, with a server-side date filter. It does not download a full city dataset.

### Cities and licences

| City | Dataset | Terms |
|---|---|---|
| New York | `data.cityofnewyork.us/rbx6-tga4` (DOB NOW: Build – Approved Permits) | See the NYC attribution below |
| Chicago | `data.cityofchicago.org/ydr8-5enu` | Disclaimer required; see below |
| Los Angeles | `data.lacity.org/pi9x-tg5x` | Provided "as is"; see below |
| Austin | `datahub.austintexas.gov/3syk-w9eu` | Portal metadata: Public Domain U.S. Government |
| Seattle | `cos-data.seattle.gov/76t5-zqzr` | Portal metadata: Public Domain |
| San Francisco | `data.sf.gov/i98e-djp9` | ODC PDDL 1.0 |
| Cincinnati | `data.cincinnati-oh.gov/uhjb-xac9` | Portal metadata: Public Domain |
| Baton Rouge | `data.brla.gov/7fq7-8j7r` | Portal metadata: Public Domain |

#### New York source and version

Source: City of New York, Department of Buildings, dataset **DOB NOW: Build – Approved Permits**, Socrata id `rbx6-tga4`. Version: the dataset as retrieved at `retrievedAt` on each row. NYC Administrative Code §23-502(d) requires a third party to identify the source and version of Open Data. This README and the fields `sourceDataset`, `sourceUrl` and `retrievedAt` are that identification.

#### Chicago disclaimer

The City of Chicago requires this disclaimer wherever an application built on its data can be accessed:

> 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.

Chicago's terms also include an indemnity clause, and the City may require a user to stop using the data. Read https://www.chicago.gov/city/en/narr/foia/data_disclaimer.html before you republish Chicago rows.

#### Los Angeles

Los Angeles publishes this data "as is". From https://data.lacity.org/terms-of-use: "This site and all materials contained on it are distributed and transmitted 'as is' without warranties of any kind, either express or implied, including without limitation, warranties of title or implied warranties of merchantability or fitness for a particular purpose." The City does not warranty the completeness, accuracy, content or fitness of any public data set on the site. No separate ban on reuse was found in that page; access is still subject to those terms.

### Good to know

- **No key.** An optional **Socrata app token** is sent as `X-App-Token` if you have one. Without it, requests share the portal's IP pool and can be throttled.
- **Person names** stay off unless you opt in. Contractor business names are part of the public permit record and are included when the city publishes them.
- **Los Angeles lag.** On 4 Oct 2026 the newest LA issue date in the feed was 26 Sep 2026. The first watch lookback for LA is 14 days so that lag does not hide the latest issued permits.
- **`:updated_at`** on these eight datasets is a bulk refresh stamp (many unrelated permits share one timestamp). Watch mode filters on the issued date after the saved cursor instead, and keeps only that day's ids.

### FAQ

**Do I need an API key?** No.

**Why did one city return `source_unavailable`?** That portal answered with an error after retries. The run still succeeds. That city's cursor is unchanged, so the next run tries it again.

**Why is valuation missing or tiny?** Some cities leave the cost blank, and Austin's `total_job_valuation` is sometimes a placeholder. The number is the city's field, not a normalised construction cost.

# Actor input Schema

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

City open-data portals to read. Empty means all eight.

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

First issued date to include (YYYY-MM-DD). Together with Issued to, this is a search and does not move the watch cursor. Leave both empty to run incremental watch mode.

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

Last issued date to include (YYYY-MM-DD). Leave both date fields empty for watch mode (only permits new since the previous run with the same Watch ID).

## `permitTypes` (type: `array`):

Normalised categories to keep. Empty means every category. A permit can also carry trade tags (solar, roof, hvac, pool, adu, ev) besides its one category.

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

Case-insensitive words matched against the work description and permit type. A permit matches if any keyword is found.

## `minValuation` (type: `number`):

Drop permits below this job value. Cities publish different fields (estimated cost, reported cost, valuation), so do not compare values across cities. Leave empty for no minimum.

## `includePersonNames` (type: `boolean`):

Off by default. When on, rows may include ownerName and applicantName where the city publishes a person's name. Contractor companies are included either way.

## `watchId` (type: `string`):

Name of the saved cursor in the permits-watch-state store. Use different IDs for different watches.

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

Cap per city per run (1-5000). Watch mode walks issued dates oldest-first so the next run continues after the saved cursor.

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

Optional Socrata application token (X-App-Token) from a Tyler/Socrata account you create yourself. Requests work without one; a token uses your own quota instead of the shared IP pool.

## `resetState` (type: `boolean`):

Ignore the saved cursor for this Watch ID and look back from scratch (3 days, or 14 days for Los Angeles).

## Actor input object example

```json
{
  "cities": [
    "new_york",
    "chicago",
    "los_angeles",
    "austin",
    "seattle",
    "san_francisco",
    "cincinnati",
    "baton_rouge"
  ],
  "issuedFrom": "2026-10-01",
  "issuedTo": "2026-10-04",
  "permitTypes": [],
  "keywords": [
    "solar"
  ],
  "minValuation": 20000,
  "includePersonNames": false,
  "watchId": "default",
  "maxItemsPerCity": 200,
  "resetState": false
}
```

# Actor output Schema

## `dataset` (type: `string`):

Dataset with one row per permit

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

Per-city status, cursors and charge-limit counts

# 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 = {
    "issuedFrom": "2026-10-01",
    "issuedTo": "2026-10-04",
    "keywords": [
        "solar"
    ],
    "minValuation": 20000
};

// Run the Actor and wait for it to finish
const run = await client.actor("mikee368/us-building-permits-watch").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 = {
    "issuedFrom": "2026-10-01",
    "issuedTo": "2026-10-04",
    "keywords": ["solar"],
    "minValuation": 20000,
}

# Run the Actor and wait for it to finish
run = client.actor("mikee368/us-building-permits-watch").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 '{
  "issuedFrom": "2026-10-01",
  "issuedTo": "2026-10-04",
  "keywords": [
    "solar"
  ],
  "minValuation": 20000
}' |
apify call mikee368/us-building-permits-watch --silent --output-dataset

```

## MCP server setup

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

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/vfwEa8egQOkeIYef1/builds/BgcGnOMJrUXuDANoO/openapi.json
