Real-Time Building Permit Alerts: Webhooks vs Polling (2026)
For a real-time permit alerts API, push beats pull. PermitStack offers a building permit webhook API (POST /v1/webhooks) that POSTs each new matching permit to your URL, plus an efficient delta feed (GET /v1/permits/events) that returns only what's new or changed. Data refreshes daily (ingest 03:00 UTC) across ~552 active jurisdictions. Shovels.ai has no webhooks; SignedOff.io has them across only ~552 jurisdictions. Start free (100 req/day, no card); paid from $19/mo.
A building permit is a lead with a shelf life. The contractor who calls the homeowner the day a solar, roofing, or HVAC permit is issued wins the job; the one who finds it three weeks later is calling someone who already signed. This post explains why permit leads decay so fast, lays out webhooks vs polling in a plain comparison table, and shows exactly how to wire up real-time permit alerts with curl and Python.
Why fresh permits decay into cold leads
Permit data is perishable in a way most B2B data is not. The value of a record is highest the moment it appears and drops sharply with every passing day, because the underlying buying decision is already in motion. When a homeowner pulls a ROOFING permit, they have usually already hired a roofer — but the surrounding signals (the neighbors who'll reroof next, the financing window, the insurance touchpoint) are live now, not next month.
For the high-velocity trades this hits hardest:
- Solar — a
SOLARpermit (we hold 5.9M) signals an active project and a whole street of lookalike rooftops; reach out in days, not weeks. - Roofing — a fresh
ROOFINGpermit (2.2M) often follows storm damage, where the homeowner is actively shopping and an insurance claim is open. - HVAC & electrical —
HVAC(2.2M) andELECTRICAL(6.0M) permits flag renovations, additions, and panel upgrades that pull in adjacent trades. - New construction — a
NEW_CONSTRUCTIONpermit (1.3M) is the starting gun for dozens of subcontractor and supplier conversations.
A two-week-old lead is a cold lead. So the engineering question isn't "can I query permits?" — every vendor can do that. It's "how do new permits reach my system the moment they exist?" That is the difference between polling and a webhook.
Webhooks vs polling: a side-by-side comparison
Polling means your code asks the API "anything new?" on a fixed schedule. A webhook flips it: the API calls you the moment something matches. Both have a place, and PermitStack supports both — here is the honest trade-off:
| Dimension | Polling (you pull) | Webhook (we push) |
|---|---|---|
| How it works | Your code calls the API on a schedule (cron, scheduled Zap) and checks for new records | PermitStack POSTs each new matching permit to your URL automatically |
| Latency to a new permit | Up to your poll interval (e.g. hourly = up to 1 hour of lag on top of ingest) | Near-real-time — fires as matching permits are ingested in the nightly run |
| API calls when nothing changed | Wasted calls every cycle (counts against your rate limit) | Zero — nothing is sent until a match exists |
| Infrastructure you run | A scheduler + state (last-seen cursor) on your side | One HTTPS endpoint (or a Zapier/Make Catch Hook URL) |
| Best for | Batch reconciliation, backfills, environments that can't accept inbound HTTP | Real-time alerts, CRM/Slack/Zapier automation, new construction lead alerts |
| PermitStack endpoint | GET /v1/permits/events (delta feed) or GET /v1/permits/search |
POST /v1/webhooks |
The smart pattern is to use both: a webhook for instant permit status webhook alerts, and a periodic poll of the delta feed as a backstop to reconcile any delivery you missed. The key is that PermitStack's poll path is a true delta feed — not a "re-download everything and diff it yourself" hack.
How PermitStack delivers real-time alerts
Three pieces work together. First, daily ingestion: we pull from city and county portals every night (ingest runs at 03:00 UTC), normalizing Socrata, ArcGIS, CKAN, CARTO, Tyler EnerGov, and Accela feeds into one schema, classifying ~20 categories and geocoding addresses. A permit a city publishes today is typically queryable — and triggering webhooks — within hours of that run.
Second, the GET /v1/permits/events delta feed. Instead of re-scanning the whole dataset, this endpoint returns only the permits that are new or changed since your last cursor — the efficient way to poll. You keep a cursor, ask "what changed?", and process a small batch.
Third, webhooks via POST /v1/webhooks. Register a target URL plus a filter, and matching permits get POSTed to you with no polling code at all. Both are introduced on our alerts landing page, and full request/response shapes live in the API docs.
Poll the delta feed (curl + Python)
If you prefer pull, poll /v1/permits/events for just the changes since you last checked. One note before you copy-paste: this feed returns transition records, not raw permit rows. Each result has an event_type (new_permit, status_change, issued, completed, expired) plus old_value/new_value, occurred_on, and detected_at — alongside the permit's permit_number, address_street, category, and jurisdiction_name. A bare call:
curl "https://api.permit-stack.com/v1/permits/events?category=SOLAR&state=CA" \
-H "X-API-Key: pk_your_key_here"
In Python, run it on a schedule and create a CRM lead for each change record in the batch:
import requests
API_KEY = "pk_your_key_here"
headers = {"X-API-Key": API_KEY}
# Pull only permits new or changed since we last checked
resp = requests.get(
"https://api.permit-stack.com/v1/permits/events",
headers=headers,
params={
"category": "SOLAR",
"state": "CA",
"per_page": 100
}
)
for event in resp.json()["results"]:
# Each result is a transition record, not a raw permit row
print(event["address_street"], event["category"], event["event_type"], event["occurred_on"])
Because you only ever receive the delta, a scheduled run stays cheap even on the free tier (100 requests/day) — you're not burning calls re-downloading permits you already have.
Register a webhook (push)
To go fully real-time, register a webhook once and stop polling. Point it at any HTTPS URL you control — or straight at a Zapier/Make hook:
curl -X POST "https://api.permit-stack.com/v1/webhooks" \
-H "X-API-Key: pk_your_key_here" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.zapier.com/hooks/catch/123/abc/",
"category": "ROOFING",
"state": "TX"
}'
From then on, every new ROOFING permit in Texas is POSTed to that URL as JSON — address, category, status, date_issued, value, and contractor_name where available — with zero code running on your side between events. That is the whole point of a building permit webhook API: the alerts come to you.
Sending permit alerts into your CRM or Zapier
You usually don't want raw JSON in a terminal — you want a row in your CRM, a Slack ping, or a line in a spreadsheet. Both paths get you there:
- No-code (recommended for most teams): create a Zapier "Catch Hook" (or Make webhook) trigger, copy its URL, and register it with
POST /v1/webhooksas above. Each new permit arrives as a trigger event; map its fields into a "Create Record" action in HubSpot, Salesforce, Pipedrive, or a Google Sheet. New construction lead alerts flow in untouched by your engineers. - Code path: if you'd rather pull, run the
/v1/permits/eventssnippet above on a schedule (a cron job or a scheduled Zap that calls the API), then push each delta record into your CRM via its own API.
Either way, the field names are stable across every jurisdiction because everything is normalized at ingest — so your Zap or mapping doesn't break when the alert comes from Austin instead of Phoenix.
How PermitStack compares on real-time alerts
This is where the market is genuinely thin, and we'll be straight about it (competitor details are publicly reported, as of June 2026):
- Shovels.ai is the category leader (2,000+ jurisdictions, Charlie AI, an MCP server) at roughly $599/mo entry, sales-gated — but it has no webhooks, and its refresh is roughly twice monthly. For a real-time alert workflow, you'd be polling a dataset that itself only updates biweekly. See our honest PermitStack vs Shovels.ai comparison.
- SignedOff.io is a closer peer on price (~$49 / ~$149) and does offer permit status webhooks — but across only about 552 jurisdictions, which is a narrow footprint for most lead-gen use cases. We break it down in our PermitStack vs SignedOff comparison.
- PermitStack offers both webhook push and a delta-poll feed across ~552 active jurisdictions (plus 51 historical-archive sources), refreshed daily, with self-serve plans from $19/mo and a free tier — no sales call.
One honest caveat on the word "real-time": no permit vendor can be fresher than the city upstream. If a jurisdiction publishes weekly, that's the ceiling for everyone. This is exactly why PermitStack tags every jurisdiction active, historical_archive, or frozen in the API and on the coverage page — so "real-time" means something specific for your area instead of a marketing word.
FAQ
How do I get real-time alerts for new building permits?
Two ways with PermitStack. Register a webhook with POST /v1/webhooks and a filter (category, city, state, zip_code) — when a matching permit is ingested in the nightly run, PermitStack POSTs the permit JSON to your URL, so new construction lead alerts land in your system automatically. If you prefer to pull, poll GET /v1/permits/events, which returns only the permits that are new or changed since your last cursor, so you never re-scan the whole dataset. Data refreshes daily (ingest 03:00 UTC), and you can start on the free tier (100 requests/day, no card).
Does Shovels.ai have webhooks?
No. As of June 2026, Shovels.ai — the category leader at roughly $599/mo entry, sales-gated — does not offer webhooks; you retrieve data by querying their API, and their refresh is roughly twice monthly (publicly reported). SignedOff.io does offer permit status webhooks but only across about 552 jurisdictions. PermitStack offers both webhook push and a /v1/permits/events delta-poll feed across roughly 552 active jurisdictions, refreshed daily, with self-serve plans from $19/mo.
How do I send permit data to my CRM or Zapier?
Point a PermitStack webhook at a Zapier (or Make) Catch Hook URL with POST /v1/webhooks and your filter. Each new matching permit is POSTed as JSON, and Zapier maps the fields — address, category, status, date_issued, contractor_name, value — straight into a CRM record, a Slack message, or a spreadsheet row. No polling code to run. If you'd rather pull on your own schedule, hit GET /v1/permits/events from a script or a scheduled Zap and create CRM records from the delta batch.
What is the difference between a webhook and polling for permit data?
Polling means your code repeatedly calls the API on a schedule to ask "anything new?" — simple to build, but it wastes calls when nothing changed and adds lag equal to your poll interval. A webhook is push: PermitStack calls your URL the moment a matching permit is ingested, so alerts are near-real-time and you burn zero calls while idle. PermitStack supports both — webhooks via POST /v1/webhooks, and an efficient delta-poll via GET /v1/permits/events that returns only new or changed permits since your last cursor.
How fast are PermitStack permit alerts?
PermitStack ingests from city and county portals daily at 03:00 UTC, so a newly published permit typically becomes available — and triggers any matching webhook — within hours of that nightly run. That is materially fresher than the roughly twice-monthly refresh publicly reported for Shovels.ai. The hard limit is the upstream jurisdiction: a city that publishes weekly caps how fresh any vendor can be, which is why PermitStack labels every jurisdiction active, historical_archive, or frozen so you know what "real-time" actually means for your area.
Get real-time permit alerts — start free
Grab a free API key (100 requests/day, no credit card), register a webhook, and route new permits into your CRM today. Paid plans from $19/mo, self-serve.
Get Free API Key