Building Permit Monitor - Construction Leads
Get alerted the moment a matching building permit is issued in Chicago, Austin, San Francisco, Seattle or NYC.
Get alerted the moment a matching building permit is issued in Chicago, Austin, San Francisco, Seattle or NYC. Filter by keywords, permit type and valuation; alerts only on never-seen permits via official city open-data APIs. Project and contractor business data, no homeowner identities.
$3.00 / 1,000 · 100.0% success over 27 public runs in the last 30 days.
Run it on Apify → Free to try - runs bill per result on your own account.
What you give it
Every field is optional unless marked; sensible defaults do the rest.
| Field | Type | What it does | Default |
|---|---|---|---|
portals |
array | Which city permit portals to monitor. All five are official open-data APIs - no keys needed. | ["chicago", "austin", "san-francisco", "seattle", "nyc"] |
keywords |
array | Alert on permits whose description or type contains ANY of these words (case-insensitive). Example: ["solar", "roof", "HVAC"]. Leave empty to match everything and filter by permit type or valuation instead. | [] |
permitTypes |
array | Only alert on permits whose type or class label contains any of these (case-insensitive substring - portal vocabularies differ). Example: ["electrical", "demolition", "new construction"]. Every dataset item carries its portal's own labels, reusable here. | [] |
minValuationUsd |
integer | Only alert on permits with a reported project cost of at least this many dollars. Permits without a published valuation always pass - portals make the field optional, and dropping them would hide real leads. | - |
lookbackDays |
integer | How many days of already-issued permits the FIRST run of a profile loads as its baseline. Later runs automatically query from the previous run with one day of overlap - re-alerting a seen permit is impossible. | 14 |
alertOn |
string | "new" alerts once per permit. "new-and-updated" also alerts when a seen permit's status changes (marked isUpdate: true), e.g. issued to finaled. | new |
emitBaseline |
boolean | On the very first run of a profile, push the currently matching permits (flagged isBaseline: true, charged at the lower baseline price) so you start with the recent picture. Turn off to seed silently and only ever receive new alerts. | True |
resetState |
boolean | Discard this profile's memory of already-seen permits and start over with a fresh baseline run. One-shot switch: turn it back off afterwards. | False |
maxItems |
integer | Upper bound on items pushed per run (baseline or alerts). On an alert run, matches beyond the cap alert on the next run. On the very first (baseline) run they are recorded as already seen instead, so raise this before the first run if you need the whole baseline. | 500 |
stateStoreName |
string | Named key-value store holding the monitor's seen-permit memory. Only change this to run fully isolated monitor instances. | permit-monitor-state |
reportFailures |
boolean | When a run fails, send the developer the actor name and build, an error category, the error message (truncated) and a one-way hash of your input - so a bug can be fixed without anyone asking you to reproduce it. Your input itself, your results and your identity are never sent, and successful runs send nothing at all. Switch this off and the actor behaves exactly the same. | True |
What you get back
One JSON object per result, with exactly these fields - no seller identities, no contact data, ever.
A real result row from a recent run:
{
"address": "1526 M L KING JR WAY",
"appliedDate": "2025-12-23",
"city": "SEATTLE",
"description": "Construct new two family dwelling per plan. (Establish use as single family residences one with an attached accessory dwelling unit per la...",
"expiresDate": "2028-04-12",
"isBaseline": false,
"isUpdate": false,
"issuedDate": "2026-10-12",
"latitude": 47.61492124,
"longitude": -122.29568016,
"permitClass": "Residential",
"permitNumber": "7116062-CN",
"permitType": "Building",
"portal": "seattle",
"state": "WA",
"status": "Issued",
"url": "https://services.seattle.gov/portal/customize/LinkToRecord.aspx?altId=7116062-CN",
"valuationUsd": 447600,
"workClass": "New",
"zip": "98122"
}
| Field | Type | Meaning |
|---|---|---|
portal |
string | Which city portal issued the permit. |
permitNumber |
string | The permit's number on its portal - its stable public identifier. |
permitType |
string | The portal's permit-type label, unnormalized. Reusable in the permitTypes input. |
permitClass |
string | Residential/commercial class where the portal publishes one. |
workClass |
string | The portal's work-class or review-type label. |
description |
string | Work description as filed with the city. |
status |
string | Current permit status on the portal. |
address |
string | Project address - where the work happens. Owner and applicant identities are never included (see the compliance section). |
city |
string | Project city. |
state |
string | Project state, two-letter code. |
zip |
string | Project ZIP code. |
latitude |
number | Project latitude where the portal publishes coordinates. |
longitude |
number | Project longitude where the portal publishes coordinates. |
valuationUsd |
number | Reported project cost in USD, when published. Absence does not mean small. |
appliedDate |
string | Date the permit was applied for, YYYY-MM-DD. |
issuedDate |
string | Date the permit was issued, YYYY-MM-DD. |
expiresDate |
string | Permit expiry date where published, YYYY-MM-DD. |
contractorName |
string | Contractor's business name, where the portal publishes one. Organization-level data only - no person names, phones or emails. |
contractorTrade |
string | Contractor's trade or role label. |
contractors |
array | All contractor parties on the permit ({trade, name}), where the portal lists several (Chicago). |
url |
string | Link to the permit record on its portal, where one exists. |
isBaseline |
boolean | True on items from a profile's first run (the recent baseline). False on real new-permit alerts. |
isUpdate |
boolean | True when the alert is a status change on an already-seen permit (alertOn: new-and-updated). |
Health
100.0% success over 27 public runs in the last 30 days. Live record, failures included: status page.
Full documentation, examples and the AI-assistant prompt live on the store page.