# Building Permit Lead Feed - 11 US Cities, Daily (`jonboyd544/building-permits-lead-feed`) Actor

New building permits from 11 official US city/county open-data portals (NYC, LA, Chicago, SF, Austin, Seattle and more) in one clean schema. Filter by work type, value and contractor; monitor mode returns only new permits.

- **URL**: https://apify.com/jonboyd544/building-permits-lead-feed.md
- **Developed by:** [Jonathan Boyd](https://apify.com/jonboyd544) (community)
- **Categories:** Lead generation, Real estate
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$5.00 / 1,000 results

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 Permit Lead Feed: 11 US Cities, One Clean Schema

Get **newly issued building permits** from 11 official city and county open-data portals in one normalized dataset. That includes NYC, Los Angeles, Chicago, San Francisco, Austin, Seattle and more. Each record carries the job value, the address, the coordinates and the **contractor business**.
Schedule it daily with **Monitor mode** and you only get permits you haven't seen before. It works as a lead feed.

### Who uses this

- **Contractors and subcontractors** (roofing, solar, HVAC, electrical, pools) find active projects and the GCs running them.
- **Building-material suppliers and equipment rental** reach contractors the week a big job is permitted.
- **Home services and insurance** spot remodels, additions and new construction in their territories.
- **Investors and analysts** track construction activity, valuations and hot ZIP codes.

### Coverage

All data comes from official government open-data portals (Socrata SODA APIs). No login, no scraping of private sites.

| Source ID | Jurisdiction | Official dataset |
|---|---|---|
| `chicago_il` | City of Chicago, IL | [data.cityofchicago.org](https://data.cityofchicago.org/d/ydr8-5enu) |
| `nyc_ny` | New York City, NY | [data.cityofnewyork.us](https://data.cityofnewyork.us/d/rbx6-tga4) |
| `los_angeles_ca` | City of Los Angeles, CA | [data.lacity.org](https://data.lacity.org/d/pi9x-tg5x) |
| `san_francisco_ca` | City and County of San Francisco, CA | [data.sfgov.org](https://data.sfgov.org/d/i98e-djp9) |
| `austin_tx` | City of Austin, TX | [data.austintexas.gov](https://data.austintexas.gov/d/3syk-w9eu) |
| `seattle_wa` | City of Seattle, WA | [data.seattle.gov](https://data.seattle.gov/d/76t5-zqzr) |
| `cincinnati_oh` | City of Cincinnati, OH | [data.cincinnati-oh.gov](https://data.cincinnati-oh.gov/d/uhjb-xac9) |
| `montgomery_county_md` | Montgomery County, MD, MD | [data.montgomerycountymd.gov](https://data.montgomerycountymd.gov/d/i26v-w6bd) |
| `new_orleans_la` | City of New Orleans, LA | [data.nola.gov](https://data.nola.gov/d/rcm3-fn58) |
| `cambridge_ma` | City of Cambridge, MA | [data.cambridgema.gov](https://data.cambridgema.gov/d/qu2z-8suj) |
| `baton_rouge_la` | East Baton Rouge Parish, LA | [data.brla.gov](https://data.brla.gov/d/7fq7-8j7r) |

Every record links back to its `dataset_url`, and to the city's permit page (`record_url`) where the city provides one.

### Input

| Field | What it does |
|---|---|
| `sources` | Which cities to query. Leave empty to query all of them. |
| `daysBack` / `dateFrom` / `dateTo` | The issue-date window. Defaults to the last 7 days. Accepts YYYY-MM-DD or forms like `30 days`. |
| `keywords` | Matches against the description, permit type, work class and contractor (e.g. `roof`, `solar`, `pool`, `demolition`). |
| `propertyClass` | `any`, `residential` or `commercial`. |
| `minValuation` / `maxValuation` | Filter by declared job value in USD. |
| `onlyWithContractor` | Keep only permits that name a contractor business. |
| `maxResults` / `maxResultsPerSource` | Overall and per-city caps. Results are sorted newest first. |
| `monitorMode` / `monitorKey` | Return only records not returned before for the same filters (or the same key). |

Example: solar and roofing jobs over $20k in Austin, Seattle and SF, as a daily feed:

```json
{"sources": ["austin_tx", "seattle_wa", "san_francisco_ca"], "daysBack": 7, "keywords": ["solar", "roof"],
 "minValuation": 20000, "monitorMode": true, "maxResults": 500}
```

### Output

Every city's records come out in one flat schema:

```json
{
  "record_id": "2776a974158b024f",
  "source_id": "chicago_il",
  "source_name": "Chicago Building Permits",
  "jurisdiction": "City of Chicago",
  "state": "IL",
  "permit_number": "101074874",
  "permit_type": "PERMIT - RENOVATION/ALTERATION",
  "work_class": null,
  "property_class": null,
  "status": "ACTIVE",
  "description": "SPR 2019 CBRC: NEW 2ND FLOOR FRAME ADDITION IN EXISTING 1.5 BRICK BUILDING WITH BASEMENT. MECHANICAL, ELECTRICAL & PLUMBING PER PLANS.",
  "date": "2026-10-30",
  "issue_date": "2026-10-30",
  "applied_date": "2025-11-11",
  "valuation_usd": 61750.0,
  "address": "1511 N MONITOR AVE",
  "city": "Chicago",
  "zip": null,
  "latitude": 41.9078598250023,
  "longitude": -87.77177140054073,
  "contractor_name": "BLUE LINE ELECTRICAL CONTRACTO",
  "contractor_trade": null,
  "contractor_phone": null,
  "owner_business_name": null,
  "housing_units": null,
  "record_url": null,
  "dataset_url": "https://data.cityofchicago.org/d/ydr8-5enu",
  "scraped_at": "2026-10-31T22:10:42+00:00"
}
```

Fields that a city doesn't publish are `null`. Coverage differs by city: Seattle rarely lists contractors, NYC rarely gives a residential/commercial class, and several cities don't publish ZIP codes.

Each run also writes a `RUN_SUMMARY` record to the key-value store. It shows each source's status (ok/failed), its error, timing and how many records it contributed, so a portal outage is visible and never silently drops data.

### Pricing

Pay per result: **$0.004 per permit** ($4 per 1,000). The run stops cleanly at your max-charge limit, and you are never charged beyond it. In monitor mode you only pay for new permits.

For comparison, as of Oct 31, 2026 other building-permit actors on Apify charge between $0.002 and $0.03 per record, and Shovels' paid plans start at $599/month for 25,000 records.

### Reliability

- Retries with exponential backoff, honours `Retry-After`, and has timeouts on every request.
- Sources are isolated: if one city's portal is down, the others still deliver, and the failure is reported in `RUN_SUMMARY` and the status message.
- Records are deduplicated across pages and runs (a stable `record_id` per city and permit number).
- Only portals with fresh data are included. Stale datasets were removed during development.

### Privacy

Only **business** names are output for contractors and owners. Names that look like an individual's are dropped, and a contractor phone is only output when a contractor business is named. Applicant names and homeowners are never output.

### FAQ

**How fresh is the data?** It is as fresh as each city's portal. Most update daily, with a lag of 1–7 days (Los Angeles typically lags about a week).
**Can I get historical data?** Yes. Set `dateFrom`, which can go back up to 10 years where the city's dataset does.
**Why are some valuations null?** Some permits (e.g. electrical or plumbing) don't declare a job value. If you set `minValuation`, permits without a value are excluded.
**Do I need an API key?** No. For very large pulls you can add a free Socrata app token to raise the rate limits.

### Limitations

- Coverage is limited to the 11 jurisdictions above; more will be added as reliable official feeds are verified.
- Field completeness varies by city (see Output).
- `property_class` is inferred from each city's permit class, or from the description when no class is given, and may be `null`.

# Actor input Schema

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

Which official portals to query. Leave empty to query all of them.

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

How many days of records to fetch when no 'Date from' is set. 0 = no date limit.

## `dateFrom` (type: `string`):

Earliest date (YYYY-MM-DD) or relative like '30 days'. Overrides 'Days back'.

## `dateTo` (type: `string`):

Latest date (YYYY-MM-DD). Leave empty for today.

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

Only keep permits whose description, type or contractor contains any of these words (e.g. roof, solar, pool, HVAC, demolition, new construction).

## `propertyClass` (type: `string`):

Filter by residential or commercial where the city provides it.

## `minValuation` (type: `integer`):

Drop permits with a declared job value below this (permits without a value are dropped too when set).

## `maxValuation` (type: `integer`):

Drop permits above this job value.

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

Keep only permits where a contractor business is listed.

## `maxResults` (type: `integer`):

Maximum records saved (and charged) in this run, newest first.

## `maxResultsPerSource` (type: `integer`):

Optional cap per source so one big city can't crowd out the others.

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

Remember what was already returned and only output records you haven't seen. Schedule the actor daily to get a fresh lead feed. State is kept per filter combination.

## `monitorKey` (type: `string`):

Optional name for the monitor state, so different saved tasks never share history.

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

Free token from any Socrata portal (dev.socrata.com) for higher rate limits on very large pulls. Not required.

## Actor input object example

```json
{
  "sources": [],
  "daysBack": 7,
  "propertyClass": "any",
  "onlyWithContractor": false,
  "maxResults": 100,
  "monitorMode": false
}
```

# Actor output Schema

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

All scraped results (overview table view)

# 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": [],
    "daysBack": 7,
    "maxResults": 100
};

// Run the Actor and wait for it to finish
const run = await client.actor("jonboyd544/building-permits-lead-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 = {
    "sources": [],
    "daysBack": 7,
    "maxResults": 100,
}

# Run the Actor and wait for it to finish
run = client.actor("jonboyd544/building-permits-lead-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 '{
  "sources": [],
  "daysBack": 7,
  "maxResults": 100
}' |
apify call jonboyd544/building-permits-lead-feed --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,jonboyd544/building-permits-lead-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/dmRd8zlfL9k8tfFgG/builds/MtdUUjHJzZhQM7gvC/openapi.json
