Skip to main content
GET
GET /v1/jurisdictions/{slug}/cohorts/distribution
Returns the canonical 22-cohort permit distribution for a single jurisdiction. The response is always 18 keys — cohorts with no permits are zero-filled. Counts reflect the rolling-window total permits classified to each cohort for that jurisdiction as of the as_of_utc timestamp.

Authentication

Authorization: Bearer <your-api-key> — see Authentication.

Path parameters

slug
string
required
Lowercase jurisdiction slug. Examples: nyc, la, houston, dc. See Concepts → Jurisdictions for the coverage list. Returns 404 for unknown slugs and for slugs in DEPLOY-PENDING status.

Response

jurisdiction_slug
string
Echoes the path parameter for round-trip clarity.
jurisdiction_display_name
string
Human-readable metro name, e.g. "New York City, NY".
as_of_utc
string
ISO 8601 UTC timestamp marking when the underlying data was last computed.
total_target_permits
number
Count of buyer-targeted permits for this jurisdiction — lifetime accumulation, no time window. See the What is total_target_permits? section below for the matview filter definition. The sum of cohort_distribution values is less than total_target_permits by the implicit NULL-segment count (permits that pass the matview filter but didn’t match any of the 18 cohort CASE branches).
cohort_distribution
object
Map of { cohort_slug: integer_count } for all 22 canonical cohorts. Always 18 keys; zero-filled when absent.

Example request

Example response

The 18 cohort_distribution values sum to 2,998,250 — the remaining 220,606 permits (matview total 3,218,856 − cohort sum) fall in the implicit NULL segment: rows that pass the matview filter but didn’t match any of the 18 cohort CASE branches in the classifier. See the explainer below for the data-model flow.

What is total_target_permits?

total_target_permits is the count of buyer-targeted commercial, multifamily, and qualifying-ADU permits for this jurisdiction. It is a lifetime accumulation — no time window applied — of permits matching PermitCore’s matview criteria:
  • work_class in (commercial, mixed, industrial), OR
  • ≥ 5 residential units, OR
  • explicit multifamily flag, OR
  • qualifying ADU
Two different totals coexist for each metro: The data-model flow:
The relationship in math: sum(cohort_distribution) + null_segment = total_target_permits. The NULL bucket isn’t exposed in the response shape; subtract the cohort sum from the total if you need to know its size for a given jurisdiction. When not to use total_target_permits:
  • For a metro’s full permit count across all work types (residential single-family, miscellaneous trades, etc.), the matview total understates by ~57% on NYC and roughly similar on other large metros. Use the metro page on permitcore.io for the lifetime silver count.
  • For time-bounded counts (e.g., “permits issued in 2025”), this endpoint is the wrong tool — use /v1/permits with the issued_after= filter. (Upper-bound until= filter is on the roadmap, not yet available.)

Errors

Errors are RFC 9457 application/problem+json — branch on the type slug (see Errors):

Caching

The upstream API responds with Cache-Control: max-age=3600. The PermitCore site consumes this endpoint via Next.js ISR with revalidate: 3600, so site-rendered pages lag the underlying pipeline by at most 1 hour beyond the pipeline’s own nightly refresh. For your own caching, the 1-hour value is a safe upper bound — pipeline data only changes once per 24 hours per metro.