Building Permit Monitor - Construction Leads
Pricing
from $1.60 / 1,000 baseline permits
Building Permit Monitor - Construction Leads
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.
Pricing
from $1.60 / 1,000 baseline permits
Rating
0.0
(0)
Developer
Lowland Data
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
2 days ago
Last modified
Categories
Share
Building Permit Monitor — Construction Leads
Watches US city building-permit portals — Chicago, Austin, San Francisco, Seattle and New York City — for newly issued permits matching your profile: keywords, permit types, minimum project valuation. It alerts the moment a matching permit is issued, and it remembers what it has already told you — so an alert always means a genuinely new project, never a re-scrape of last week's list.
It reads each city's official open-data API (the access route the cities publish for exactly this), and the output is clean by construction: project, address, valuation and contractor business — never homeowner or applicant identities. Safe to store, share with your sales team, or import into a CRM.
Quick start (30 seconds, no account keys needed)
- Run the actor with the prefilled input (Seattle, last 14 days) — it finishes in a few seconds and shows a baseline of recent permits.
- Replace the demo profile with your own: your cities, your trade's keywords or permit types, your minimum project size.
- Add a daily schedule (Actor page → Schedule). From then on, runs are quiet until a permit you care about is actually issued.
Who uses this
- A roofing contractor in Chicago monitors
keywords: ["roof", "reroof"]daily. When a permit is issued two neighborhoods over, the alert lands with theaddress,valuationUsdandissuedDate— a lead the same week the project goes live, not after the crews are already on site. - A solar installer in Austin watches
keywords: ["solar", "photovoltaic"]— every new solar permit shows whichcontractorNamewon the job, mapping both leads and competitors in one feed. - A building-materials supplier runs
permitTypes: ["new construction"]withminValuationUsd: 500000across all five cities — only projects big enough to supply, straight into the CRM via the API. - A market analyst tracks permit volumes and valuations per city as a construction-economy indicator — the baseline plus daily deltas is a clean time series.
- An HVAC company pairs
permitTypes: ["mechanical"]withalertOn: "new-and-updated"to also see status changes on projects it quoted.
What you get
One JSON item per event: a baseline snapshot per permit on the first run, then only transitions — newly issued matching permits, and (opt-in) status changes on permits you have already seen. A new-permit alert looks like:
{"portal": "austin","permitNumber": "2026-091811 EP","permitType": "Electrical Permit","permitClass": "Residential","workClass": "New","description": "Install rooftop solar array, 24 panels, 9.6 kW","status": "Active","address": "8316 ROBERT ZION WAY","city": "Austin","state": "TX","zip": "78744","valuationUsd": 48500,"appliedDate": "2026-07-16","issuedDate": "2026-07-17","expiresDate": "2027-01-13","contractorName": "Powerhouse Electrical Services, Inc.","contractorTrade": "Electrical Contractor","url": "https://abc.austintexas.gov/web/permit/public-search-other","isBaseline": false,"isUpdate": false}
Field honesty notes:
- Every portal has its own vocabulary —
permitType,permitClassandworkClasscarry the city's own labels, unnormalized, and every item's labels are reusable as yourpermitTypesfilter. valuationUsdappears only when the city publishes a project cost — many permits carry none, and absence does not mean small.contractorNameis the business on the permit where the city publishes one (Chicago and Austin do; San Francisco and Seattle mostly don't).addressis the project site the city publishes for public accountability. Who lives there is deliberately not in the data — see the compliance section.
How much does permit monitoring cost?
Pay per outcome — checking costs nothing; you pay when the monitor tells you something new:
- New matching permit (the alert): $0.02 — also for status-change alerts.
- Baseline item (first run only): $0.002.
- Actor start: $0.005. Everything included - there are no separate platform-usage charges, and higher Apify plans get automatic unit-price discounts.
Cost feel: a focused trade profile in two cities sees a handful of new permits per working day → roughly $2–8/month for complete coverage, with the one-time baseline (say 100 permits) costing about $0.20. A run with zero new matches costs $0.005 all-in. Lead-gen services sell the same permit leads for $50–300/month.
Why an alert costs more than a scraped row
The store's headline ("from $2.00 / 1,000 baseline results") is what a trial costs: a 100-permit baseline ≈ $0.20. Apify displays every event per 1,000, so in the pricing table the alert event reads "$20.00 / 1,000" next to raw permit scrapers at $2–30 per 1,000 rows — but that comparison is between different things:
- A raw permit scraper sells rows: to monitor with rows you re-download each city's recent permits every day and diff them yourself.
- This monitor sells the transition: it remembers state in your account and charges $0.02 exactly when a permit you have not seen matches your profile. Nobody buys 1,000 alerts a month.
Not technical? Let your AI assistant set it up
Paste this prompt together with a line about your trade and cities:
Set up the Apify actor lowlanddata/building-permit-monitor to watch US buildingpermits for me. Build the input JSON from my business description:- portals: which of ["chicago","austin","san-francisco","seattle","nyc"] I can serve- keywords: 3-8 words my kind of project's description would contain(e.g. ["roof","reroof"] for roofing, ["solar"] for solar installers)- permitTypes: my trade's permit labels if I named any (e.g. ["electrical"])- minValuationUsd: only if I gave a minimum project sizeLeave alertOn, emitBaseline and maxItems at their defaults. Run it once andshow me the baseline as a table (description, address, city, valuation,contractor, issued date). Then create a daily schedule for it and set up anotification so I get an email when a run produces items.
Coverage — measured, not promised
- Sources: each city's official open-data permit dataset (Socrata APIs) — Chicago, Austin, San Francisco, Seattle and New York City (DOB NOW). Keyless, sanctioned access; together several thousand permits/week unfiltered (measured July 2026, NYC alone ~3,000/week).
- More cities are on the roadmap; each city ships only after its dataset passes the same field-level privacy verification.
- Cities update their datasets on their own cadence — typically daily with a small lag after issuance. The monitor alerts when the dataset publishes the permit; that is as fast as official data allows.
- Permit-type vocabularies differ per city —
permitTypesis an honest substring match over each city's own labels, not a fake universal taxonomy. - A portal outage or dataset migration never fails the run: the affected city is reported in the run status (uncharged) and its permits alert once it recovers.
How the monitor thinks
- First run = baseline. It fetches your profile's matches from the last
lookbackDays(default 14), pushes them flaggedisBaseline: trueat the low baseline price, and memorizes them.emitBaseline: falseseeds silently. - After that, only transitions. Each run queries from the previous run (one
day of overlap; the memory absorbs duplicates) and pushes only permits it has
never alerted. Status changes alert as
isUpdate: truewhenalertOn: "new-and-updated"; with the default they stay silent but are remembered. - Editing the profile starts a fresh baseline — matching-relevant inputs
are the profile's identity, so a changed profile can never flood you with
alerts computed against another profile's memory.
maxItems,emitBaselineand schedule changes do not restart anything. - Memory lives in a named key-value store (
stateStoreName, defaultpermit-monitor-state) in your account. Run several profiles on separate schedules against the same store — they never interfere.resetState: truewipes the current profile's memory. - A capped baseline samples every selected city round-robin instead of exhausting the first one.
- Re-running after a crash or overlap re-alerts at most the last few unsaved items — state is checkpointed during the run.
Input reference
| Field | Type | Default | Notes |
|---|---|---|---|
portals | array | all five | chicago, austin, san-francisco, seattle, nyc. |
keywords | array | [] | OR, case-insensitive, over description + type labels. |
permitTypes | array | [] | Substring match over the city's type/class labels only. |
minValuationUsd | integer | — | Permits without a published valuation always pass. |
lookbackDays | integer | 14 | Baseline reach on the first run (max 60). |
alertOn | string | "new" | "new-and-updated" adds status-change alerts. |
emitBaseline | boolean | true | false = silent seeding. |
resetState | boolean | false | One-shot: wipe this profile's memory. |
maxItems | integer | 500 | Overflow alerts on the next run, never lost. |
stateStoreName | string | "permit-monitor-state" | Only change to fully isolate monitor instances. |
Scheduling and API use
Run it daily and pair the schedule with a Monitoring alert on "dataset items
0" to get an email exactly when a permit matched.
curl -X POST \"https://api.apify.com/v2/acts/lowlanddata~building-permit-monitor/run-sync-get-dataset-items?token=$APIFY_TOKEN" \-H 'Content-Type: application/json' \-d '{"portals": ["austin"], "keywords": ["solar"]}'
import { ApifyClient } from 'apify-client';const client = new ApifyClient({ token: process.env.APIFY_TOKEN });const run = await client.actor('lowlanddata/building-permit-monitor').call({portals: ['chicago', 'austin'],keywords: ['roof', 'reroof'],});const { items } = await client.dataset(run.defaultDatasetId).listItems();console.log(items); // only new permits since the last run
from apify_client import ApifyClientclient = ApifyClient(os.environ["APIFY_TOKEN"])run = client.actor("lowlanddata/building-permit-monitor").call(run_input={"portals": ["seattle"],"permitTypes": ["mechanical"],})items = client.dataset(run["defaultDatasetId"]).list_items().items
Daily runs finish in well under a minute, comfortably inside the 300s sync-call wall; a 60-day four-city baseline is the one case that can take a couple of minutes — run it async once.
Use it with AI agents (MCP)
Claude, Cursor and other MCP-capable agents can run this monitor as a tool through Apify's hosted MCP server: the agent builds the profile from a plain-language brief, runs it, and reads the leads back — no glue code.
Claude Code:
$claude mcp add apify --transport http "https://mcp.apify.com?actors=lowlanddata/building-permit-monitor"
Cursor or Claude Desktop (add a custom connector / MCP server with this URL):
https://mcp.apify.com?actors=lowlanddata/building-permit-monitor
Sign in with your Apify account when prompted — runs are billed to it. Setup details per client: Apify MCP docs, or start from this actor's own MCP page: apify.com/lowlanddata/building-permit-monitor/api/mcp.
Prompts that work once connected:
- "Watch Chicago and Austin for new roofing permits and show me today's leads."
- "What solar permits over $30k were issued in Austin this week, and which contractors got them?"
- "Track new-construction permits over $1M across all five cities and alert me daily."
Is it legal to monitor building permits?
Yes — building permits are public records, and these five cities publish them through official open-data APIs exactly so they can be reused. No login walls, no anti-bot bypass — if a portal is unreachable, the run reports it rather than fighting it. And the output is safe by construction: homeowner and applicant identities are never extracted (see below).
How you contact a lead is on you — this tool gives you the public project record, not marketing-law advice for your state.
Compliance: safe to store, safe to CRM-import
Permit records as published by the cities can contain personal data — owner and
applicant names, phone numbers, even the applicant's own address. This actor's
guarantee is structural: each city's mapper whitelists fields IN (project,
address, valuation, dates, contractor business name/trade), so person-fields
never map at all, and an automated test proves it against each city's real
column names. The project address stays — it is the publicly published
project site and the lead's core value; who lives there is deliberately
unreachable. One honest limit: description is text the applicant wrote — on
rare occasions someone writes a name into free text. We cannot rewrite public
records; everything structured is guaranteed clean.
FAQ
Why did my second run return nothing? Because nothing new matched — that is the product working. The first run is the baseline; afterwards you only pay for permits you have not seen. The run status says how many permits were checked.
Does the output contain homeowner names or phone numbers? No — by design. The city mappers never touch owner/applicant fields. You get the project, the address, the valuation and the contractor business.
Which cities are covered? Chicago, Austin, San Francisco, Seattle and New York City — cities with official open-data permit APIs. More follow (New York next); tell us which city you need via the Issues tab.
Can I monitor several trades or cities separately? Yes — one schedule per profile, same actor, same state store. Each profile keeps its own memory.
How fresh are the alerts? As fresh as the city's dataset — typically the permit appears within a day or two of issuance. The monitor can't be faster than the official data, and doesn't pretend to be.
What does a status-change alert mean? With alertOn: "new-and-updated",
a seen permit whose status moves (e.g. Active → Finaled) alerts once as
isUpdate: true — useful for tracking projects you quoted.
Troubleshooting
- "Every portal failed" — the open-data platforms are down or unreachable; re-run later. Single-portal failures never fail the run.
- " — the lookback window matched
more than ~10,000 permits; lower
lookbackDaysor narrow the profile. - "Monitor state record … is not readable" — the state was written by a
newer version; run once with
resetState: trueto start a fresh baseline.
Something else off? Open an issue on the actor's Issues tab — reports are read and answered.