Permits (listing endpoint)
API Reference
Permits (listing endpoint)
Paginate permit rows by jurisdiction × date with the full normalized + source + enrichment fields.
GET
Permits (listing endpoint)
Returns paginated permit rows filtered by jurisdiction + date range.
The response example below is generated from the live OpenAPI spec
at api.permitcore.io/openapi.json —
re-pulled before each docs deploy, never hand-written.
Note on List-vs-detail asymmetry: the nested
The nested
See Get a permit by canonical_uid.
Note that the top-level PLUTO-derived fields (The
Authentication
Authorization: Bearer <your-api-key> — see Authentication.
Query parameters
| Param | Type | Notes |
|---|---|---|
jurisdiction | string (slug) | Required. Single metro slug, e.g. nyc, la. |
target_segment | string (slug) | Optional. Curated cohort filter for the /v1/permits/target variant — see the variant section below. (The plain cohort= parameter on this endpoint arrives with the next promotion.) |
issued_after | ISO 8601 date | Optional. Lower bound for issued_date (YYYY-MM-DD). |
per_page | integer | Optional. Page size; max 500. |
page | integer | Optional. 1-indexed page number. |
Response shape
The example below is generated from thePermitResponse schema in
openapi.json. The enrichment fields (cohort, dq_flags, top-level
PLUTO-derived fields, description_llm_enriched) are now on the wire as
of the 2026-06-04 promotion. The nested pluto DQ-status object is
list-endpoint NULL by design — see the list-vs-detail asymmetry note below.
total_count: capped at 1,000 on selective-filter queries. When pagination.has_more is true, the true total exceeds the cap — render as "1,000+" if you surface this number directly.
What’s on the wire
The list-endpoint shape carries 53 fields organized as:- Identity —
id,jurisdiction_slug,source_permit_id,canonical_uid,filing_id - Address —
address_full,address_street,address_city,address_state,address_zip,parcel_apn - Geocode metadata —
lat,lng,geocode_confidence,geocode_source - Type + classification —
permit_type_raw,permit_type_normalized,work_class,building_subtype,is_new_construction,is_alteration,is_demolition - Scope —
units_residential,units_commercial,units_total,square_footage_total,valuation_usd - PLUTO-derived (denormalized) —
year_built,building_class,zoning_district,lot_area - Dates —
applied_date,issued_date,finaled_date,expired_date - Status + description —
current_status,status_raw,description_raw,description_extracted,description_llm_enriched - People —
applicant_name,applicant_role,contractor_name,contractor_license,architect_name,engineer_name,owner_name - Source link + timestamps —
source_url,first_seen_at,last_seen_at,last_changed_at - Enrichment —
cohort,dq_flags,pluto
Enrichment field semantics
cohort— the buyer-targeting bucket the row classifies into, one of the 18 canonical cohorts.null= row not in the cohort matview OR in the matview but unsegmented. Identical taxonomy totarget_segmenton/v1/permits/target. See Concepts → Cohorts.dq_flags— 12-key boolean object from the bounds-rule registry. All 12 keys are always present when this field is non-null. Semantic note:falsemeans “the rule has not flagged this value,” NOT “the value was independently verified clean.” NULL silver flag values are coerced tofalseper the SD-039 rule. See Data freshness → field-level DQ flags for the full flag set + semantics.description_llm_enriched— LLM-extracted feature object (e.g.scope_summary,unit_count,square_footage,tenant_improvement_flag). Distinct column fromdescription_extracted(which is adapter-populated, per-jurisdiction shape). Coverage today: Austin + Chicagocommercial_newpermits only (~17,861 prod rows). Broader cross-metro LLM extraction is on the roadmap.
List-vs-detail asymmetry: the nested pluto object
The nested pluto object (PLUTO parcel-data presence + per-flag DQ
status — {is_placeholder, oob}) is ALWAYS null on /v1/permits
by design.
The PLUTO LEFT JOIN required to populate the DQ-status object would
add ~75ms p95 to the list query, which disqualifies it for the browse
path. To get the populated pluto object, query the per-permit detail
endpoint:
year_built,
building_class, zoning_district, lot_area, units_total) are
denormalized into the silver layer and DO populate on this endpoint
when known. Only the nested pluto DQ-status object is detail-only.
The /v1/permits/target variant
/v1/permits/target accepts the same parameters but applies a curated
commercial/multifamily/ADU filter via target_segment=<cohort>. This is
the cohort-classified slice that the site’s recency surfaces query.
target_segment accepts all 18 canonical cohort slugs. Behavior:
- Valid cohort with zero rows in window:
200 OKwith an emptyitemsarray — no404. Usepagination.total_countto confirm the empty result. - Invalid slug (not in the 18-cohort enum):
422 Unprocessable Entitywith a problem+json body enumerating the accepted whitelist.
Quotas
Quotas are tier-based and apply to every request against this endpoint. See Rate limits for the matrix + theX-RateLimit-* response headers + the 429 problem+json shape.
Example request
Related
- Get a permit by canonical_uid — the per-permit detail endpoint with full enrichment + nested
pluto. - Cohort distribution — aggregate counts behind these rows.
- Free sandbox — client-side DuckDB-WASM queries against the last 10 days of permits across the live metros. No signup, no API key.