P&Z Radar API
Build on the same data your alerts run on: the validated rezonings, specific use permits, plats and PD amendments we track across Miami P&Z agendas, as clean JSON. Same pipeline, same freshness — available as each packet is captured.
Base URL: https://miami.zoningfirehose.com. The API stays on that host; your API key and base URL are unchanged by the website's move, and API calls are never redirected.
API access is included with the Metro and Corporate plans. Sign in and mint a key on your account page — it takes a moment and the key is shown once. Email us if anything goes wrong.
Try it live
The whole API is published as OpenAPI 3, so you can explore it here or generate a client instead of hand-writing one. The spec declares the X-API-Key scheme, so Swagger's Authorize button and any generated client send it for you — and it is scoped to Miami, the metro you are viewing, so the examples call the host your plan covers.
The explorer calls this site's metro directly. Download /openapi.json for generated clients using the stable zoningfirehose.com API addresses.
Open interactive Swagger UI → ReDoc reference · openapi.json
Authentication
Send your key in the X-API-Key header on every request. Requests without a valid key get 401.
Rate limit: 60 requests/minute per account (300 on Corporate, shared across its users). The quota belongs to your account, not to each key — minting a second key gives you another credential, not more requests. Over the limit returns 429; retry after the Retry-After seconds. Every response carries X-RateLimit-Remaining so you can back off first.
curl -H "X-API-Key: zf_your_key_here" \
"https://miami.zoningfirehose.com/api/v1/cases?county=Broward&case_type=rezoning&limit=5"Endpoints
GET /api/v1/cases
List validated cases, newest first. Returns total (matches across all pages), count, cases, and ready-to-follow next/prev page URLs (null at the ends). Every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset so you can back off before a 429. The API is metro-scoped by host — this host serves Miami data; hit another metro's subdomain for its data (each case carries a metro field).
Query parameters (all optional):
| Param | Meaning |
|---|---|
municipality | City name, e.g. Atlantis |
county | Broward, Miami-Dade, Palm Beach, St. Lucie |
petitioner | Exact applicant name. The example Example Development LLC is fictional; use an actual petitioner value returned by your paid GET /api/v1/cases call, or browse the public archive directory. |
case_type | One of: rezoning, specific_use_permit, plat, pd_amendment, comprehensive_plan_amendment, variance, site_plan, text_amendment, annexation, other |
status | One of: new, updated, continued, approved, denied, withdrawn. Only new, updated, continued are ever produced — see below |
hearing_after / hearing_before | ISO dates (YYYY-MM-DD) bounding the hearing date |
q | Keyword search across case number, structured fields, and full agenda text |
limit / offset | Paging — limit defaults to 50, max 200. Above 200 returns 422 rather than silently truncating, so a short page is always a page you asked for. |
Each case object carries the full extracted record:
{
"case_number": "Z-2026-014",
"case_type": "rezoning",
"address_or_location": "NE corner of Main St & FM 2478",
"acreage": 12.4,
"current_zoning": "AG",
"requested_zoning": "PD-MF",
"proposed_use": "Multi-family residential",
"petitioner": "Example Development LLC",
"staff_recommendation": "approval",
"confidence": "high",
"impact_summary": "Rezoning 12.4 acres from agricultural to a planned development for multi-family.",
"municipality": "Atlantis",
"county": "Broward",
"metro": "miami",
"hearing_date": "2026-08-11",
"status": "new",
"processed_at": "2026-07-28T14:02:11Z",
"detail_url": "https://miami.pnzradar.com/cases/atlantis/Z-2026-014",
"source_url": "https://atlantis.legistar.com/..."
}GET /api/v1/cases/{municipality}/{case_number}
One case by city and case number. 404 if we don't track it.
curl -H "X-API-Key: zf_your_key_here" \
"https://miami.zoningfirehose.com/api/v1/cases/atlantis/Z-2026-014"GET /api/v1/municipalities
Coverage list: every municipality, its county, agenda platform, P&Z meeting cadence, live status, and tracked case count.
curl -H "X-API-Key: zf_your_key_here" "https://miami.zoningfirehose.com/api/v1/municipalities"Data Center exports
The Data Center section (its own plan or trial) has two export routes, scoped to the account and the metro of the host you call. They authenticate with the account's read-only Data Center export token in the token query parameter (shown on your profile page, and resettable there) — not with X-API-Key. The export token opens only the exports, never your settings; the older Data Center link token is still accepted. A token without access gets 403, an unknown one 404. Both are declared in the OpenAPI spec under the tag Data Centers.
GET /data-centers/brief.json
One JSON object: `period_start`, `period_end`, `coverage`, `source_health_notes`, `count`, `undated_count`, `metro` and `events` -- one object per record with the fields `project_id`, `event_id`, `evidence_hash`, `review_state`, `project_name`, `campus`, `phase`, `municipality`, `county`, `address_or_location`, `developer`, `applicant`, `owner`, `provenance`, `observation_context`, `source_url`, `source_page_or_section`, `sources`, `source_published_at`, `meeting_date`, `first_observed_at`, `stage`, `stage_actor`, `what_changed`, `megawatts`, `capacity_claims`, `equipment_notes`, `approvals_notes`, `supplier_selection_notes`, `inferred_buyer_relevance`, `customer_next_step`, `buyer_relevance`, `next_decision`, `action_record`, `factors`, `next_decisions`, `unknowns`, `record_class`, `published_at`. `review_state` is the store's decision, ids are stable across exports, and `unknowns` is a list. `start`/`end` (YYYY-MM-DD, inclusive; default: the trailing week) bound the window on the date each record became visible to you: when it was first observed, or the day it was published if that came later. Scoped to the metro of the host you call and to the account's saved territory preferences (tracked projects always included). Every response carries `X-Coverage` (complete / impaired / unknown), `X-Coverage-Period`, `X-Coverage-Note-Count` and `X-Coverage-Notes`: the verdict on whether the collector read every watched city in the window, which is the difference between 'nothing was filed' and 'we could not look'. Only approved, current records are ever returned; a superseded version never is. `preferences_applied` (JSON) and the empty-file marker row (CSV) say whether a saved preference removed records from the window, as distinct from a period in which nothing was filed.
GET /data-centers/brief.csv
One CSV row per record with the columns `project_id`, `event_id`, `evidence_hash`, `review_state`, `project_name`, `campus`, `phase`, `municipality`, `county`, `address_or_location`, `developer`, `applicant`, `owner`, `provenance`, `observation_context`, `source_url`, `source_page_or_section`, `sources`, `source_published_at`, `meeting_date`, `first_observed_at`, `stage`, `stage_actor`, `what_changed`, `megawatts`, `capacity_claims`, `equipment_notes`, `approvals_notes`, `supplier_selection_notes`, `inferred_buyer_relevance`, `customer_next_step`, `buyer_relevance`, `next_decision`, `action_record`, `factors`, `next_decisions`, `unknowns`, `record_class`, `published_at`, then the coverage cells `export_metro`, `export_period_start`, `export_period_end`, `export_coverage`, `export_coverage_notes` on every row. Cells never blank: `N/A` means the field cannot apply to this record, `Unknown` means it could and was not found. Cells that would start with a formula trigger are prefixed with an apostrophe. The filename carries the metro, the period and the coverage verdict. `start`/`end` (YYYY-MM-DD, inclusive; default: the trailing week) bound the window on the date each record became visible to you: when it was first observed, or the day it was published if that came later. Scoped to the metro of the host you call and to the account's saved territory preferences (tracked projects always included). Every response carries `X-Coverage` (complete / impaired / unknown), `X-Coverage-Period`, `X-Coverage-Note-Count` and `X-Coverage-Notes`: the verdict on whether the collector read every watched city in the window, which is the difference between 'nothing was filed' and 'we could not look'. Only approved, current records are ever returned; a superseded version never is. `preferences_applied` (JSON) and the empty-file marker row (CSV) say whether a saved preference removed records from the window, as distinct from a period in which nothing was filed.
curl "https://miami.zoningfirehose.com/data-centers/brief.csv?token=YOUR_EXPORT_TOKEN&start=2026-09-01&end=2026-09-07"MCP server — use the data from Claude
Ship your key to Claude Desktop, Claude Code, or any MCP client with our single-file server — no extra packages beyond Python 3.11+ and httpx. Download mcp_server.py, then add this to your MCP config:
{
"mcpServers": {
"zoning-firehose": {
"command": "python",
"args": [
"/path/to/mcp_server.py"
],
"env": {
"ZONING_API_KEY": "zf_your_key_here",
"ZONING_API_BASE": "https://miami.zoningfirehose.com"
}
}
}
}That gives Claude four tools — search_cases, get_petitioner_history, get_case, and list_municipalities — so you can ask things like “any multi-family rezonings in Broward County with hearings next month?” or “what has this applicant filed this year?” and get grounded answers from live data.
No-code: RSS into Slack, Teams or Zapier
Every plan — not just metro — includes a private RSS feed of your matches at /feed.xml?token=…, running the same filters as your email. The link is on your filters page. No API key required.
- Slack: type
/feed subscribefollowed by your feed URL. New cases post straight into the channel — for a team that beats a text to one person. - Teams: add an RSS connector to a channel and paste the same URL.
- Zapier / Make: use the New Item in Feed RSS trigger to push cases into a CRM, a spreadsheet, or anything else you already run.
Treat the feed URL like a password — anyone holding it sees the cases your filters match. It is read-only and separate from your sign-in link, so it cannot change your alerts or show your address. If it leaks, reset it yourself from your account page — one click, and the old URL stops working immediately.
Lifecycle, signals and proximity in the data
Cases are tracked as they move, so the same case number legitimately appears more than once over its life. status reflects the latest transition we observed — a case advancing from Planning & Zoning to Council is a distinct event from a continuance, and the two are never conflated.
The pipeline does not infer vote outcomes from agendas. Agendas are published before the meeting. The status filters match stored rows, including approved, denied and withdrawn where present; they do not establish complete outcome coverage. An empty result means no matching stored rows, not evidence that no case was approved. Treat the absence of an outcome as missing data — we do not know — rather than a negative result.
staff_recommendation is the field to watch if you are modelling outcomes, but it is a pre-hearing opinion, not a final decision. A value of deny is the earliest reliable signal a case is in trouble, and it arrives with the packet rather than at the hearing. It is present on roughly a third of cases — only where the packet states one, never inferred.
Proximity alerts (filings near land you own) are an email and RSS feature configured on your filters page; they are not part of the REST payload.
Fair use
Data is licensed for your internal tools and analysis under your subscription. No resale or public rehosting of the feed. Questions, higher limits, webhooks? questions@pnzradar.com.
P&Z Radar AI agent · Expand
Hi, I’m Pip. How can I help?