Developer docs
Published zoning content as structured data: municipalities, districts, rules, and reviewed Q&A, every rule with its ordinance citation and source link, plus address-to-district resolution where a public GIS path exists.
Records tell you what's on file; ZoningVerdict tells you what the ordinance actually says, independently verified, with the citation.
ZoningVerdict summarizes public zoning regulations for information purposes. It is not legal advice, professional architectural or engineering services, or a substitute for confirmation with the municipality. Regulations change; verify before acting.
Every response carries that disclaimer and the content pack's last-reviewed date and version, so agents can cite and cache coherently. API response disclaimer fields link to /terms (v0-draft). Draft content never appears through any path.
Authentication
The municipalities and districts endpoints are free and keyless. Rules, questions, and parcel resolve need a key from /api-access (self-serve, shown once), sent as a Bearer token or an X-Api-Key header. Each key includes 100 free calls per month; after that, $0.10 per rules or questions call and $0.25 per parcel resolve, billed through Stripe.
OpenAPI
The schema is served at https://zoningverdict.com/api/v1/openapi.json.
Agent discovery
Machine-readable instructions at /skill.md, A2A discovery at /.well-known/agent-card.json, and the x402 paid-tool manifest at /.well-known/ai-tool/index.json. Terms: https://zoningverdict.com/terms.
Try it
Live calls against the real API. Discovery is free and keyless; rules and questions have a 3-call daily keyless trial per municipality. Parcel resolve returns the real 402 from the first unsigned request. Errors are included; nothing is staged.
GET /api/v1/districts?municipality=MCP: list_districts {"municipality":""}
Endpoints
GET /api/v1/municipalities
Published coverage with pack version and last-reviewed date. Free.
curl https://zoningverdict.com/api/v1/municipalities
GET /api/v1/coverage/check?q=
Coverage preflight for an address or municipality. Free; does not identify a district.
curl "https://zoningverdict.com/api/v1/coverage/check?q=1000%20Rochester%20Hills%20Dr%2C%20Rochester%20Hills%2C%20MI"
GET /api/v1/districts?municipality=
District codes, names, and summaries for one municipality. Free.
curl "https://zoningverdict.com/api/v1/districts?municipality=rochester-hills"
GET /api/v1/changelog
Content changelog: publications, pack updates, and published rule changes, newest first. Free.
curl https://zoningverdict.com/api/v1/changelog
GET /api/v1/rules?municipality=&district=&topic=
Structured rules with citations and source URLs. Metered.
curl -H "Authorization: Bearer zvk_..." \ "https://zoningverdict.com/api/v1/rules?municipality=rochester-hills&district=R-2&topic=fences"
GET /api/v1/questions?municipality=&topic=
Reviewed question-and-answer pairs with citations. Metered.
curl -H "Authorization: Bearer zvk_..." \ "https://zoningverdict.com/api/v1/questions?municipality=rochester-hills&topic=adu"
POST /api/v1/parcel/resolve
Address to municipality and district, lookup method disclosed. x402 pay-per-call from the first unsigned request; run the free coverage preflight first.
curl -X POST https://zoningverdict.com/api/v1/parcel/resolve \
-H "Authorization: Bearer zvk_..." -H "content-type: application/json" \
-d '{"address": "1000 Rochester Hills Dr, Rochester Hills, MI"}'Recommended flow for agents
Never guess district codes: coverage and listing calls are free, then the paid address resolver identifies the district. Every empty result carries a note naming the valid district codes or covered topics, so the next call can succeed.
# Address-first (the usual case)
1. GET /api/v1/coverage/check?q=... -> covered? topics + freshness
2. POST /api/v1/parcel/resolve {"address": "..."} -> paid municipality + district_code
3. GET /api/v1/rules?municipality=...&district=... -> rules with citations
4. GET /api/v1/questions?municipality=... -> reviewed Q&A
# No address? Discover, then fetch (discovery is free and keyless)
1. GET /api/v1/municipalities -> covered slugs
2. GET /api/v1/districts?municipality=... -> valid district codes
3. GET /api/v1/rules?municipality=...&district=...
Same flow over MCP: check_coverage -> resolve_parcel_district -> get_district_rules,
or check_coverage -> list_municipalities -> list_districts -> get_district_rules.MCP server
The same content is exposed as MCP tools — check_coverage, list_municipalities, list_districts, get_district_rules, ask_zoning_question, and resolve_parcel_district — over the streamable HTTP transport. The coverage and listing tools are free and keyless. Every response carries the disclaimer, pack version, last-reviewed date, and ordinance source link. The paid property brief is deliberately not an MCP tool: the brief stays a human-purchase surface.
Endpoint: https://zoningverdict.com/api/mcp Auth: Authorization: Bearer zvk_... (same keys as the REST API) Trial: rules and questions only, 3 calls per day per municipality Free: check_coverage, list_municipalities, and list_districts never count
Agent payments (x402)
Agents can also pay per call, no key or account needed. A keyless rules or questions call gets 3 free calls per day per municipality. Parcel resolve is pay-per-call from the first unsigned request; use the free coverage preflight before it. When payment is due, the endpoint returns HTTP 402 with a PAYMENT-REQUIRED header carrying x402 protocol version 2 payment requirements: $0.01 in USDC on Base, exact scheme. Retry with a PAYMENT-SIGNATURE header from any x402 v2 client; the settlement transaction comes back in the PAYMENT-RESPONSE header. Keyless x402 is the lowest per-call rate today; keys add 100 free calls per month, predictable Stripe billing, and higher rate limits. Flat monthly plans with volume pricing are coming.
# One paid call with the x402 fetch wrapper (@x402/fetch):
Price: $0.01 per call, USDC on Base
Endpoints: /api/v1/rules, /api/v1/questions, /api/v1/parcel/resolve
Flow: request -> 402 + PAYMENT-REQUIRED -> sign -> retry with
PAYMENT-SIGNATURE -> 200 + PAYMENT-RESPONSE (settlement)Limits
- Per-key daily limit (default 1,000 calls), reset at midnight UTC.
- Published content only. Topics: permitted_uses, special_land_uses, setbacks, height, lot_coverage, lot_size, parking, adu, accessory_structures, fences, signs, home_occupation, variance_process.
- HTTP errors return {error, message} with conventional status codes.
- Empty results are teaching results: an unknown district returns the valid district codes, and a query with no reviewed answer returns the topics that do have one.
Change monitoring
Every pack carries a version and last-reviewed date, and the ordinance behind it is watched for amendments. A $49/month watch emails you when a human-reviewed rule change is published for a municipality — citation, date, and pack version included. Webhook delivery and an MCP create_watch tool are planned; watch mail is the v1 lane. Data vintage is public: per-municipality freshness on the coverage page and content events at /api/v1/changelog, both free and keyless.
Need a municipality we do not cover?
City Unlock records the demand for it. When a municipality enters the reviewed pipeline, it runs from the official ordinance through independent verification and live GIS testing (see how we verify a municipality). The result becomes public API coverage maintained under our amendment watch.