> ## Documentation Index
> Fetch the complete documentation index at: https://docs.permitcore.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Permits (listing endpoint)

> Paginate permit rows by jurisdiction × date with the full normalized + source + enrichment fields.

Returns paginated permit rows filtered by jurisdiction + date range.
The response example below is **generated from the live OpenAPI spec**
at <a href="https://api.permitcore.io/openapi.json">api.permitcore.io/openapi.json</a> —
re-pulled before each docs deploy, never hand-written.

## Authentication

`Authorization: Bearer <your-api-key>` — see [Authentication](/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 the `PermitResponse` 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.

```json theme={null}
{
  "items": [
    {
      "id": 4218034,
      "jurisdiction_slug": "nyc",
      "source_permit_id": "BT-202601090",
      "canonical_uid": "1:BT-202601090",
      "filing_id": null,

      "address_full": "82 ASP 1ST FLOOR, Manhattan, NY 10009",
      "address_street": "82 ASP",
      "address_city": "Manhattan",
      "address_state": "NY",
      "address_zip": "10009",
      "parcel_apn": "1004920019",
      "lat": 40.7271,
      "lng": -73.9836,
      "geocode_confidence": 0.92,
      "geocode_source": "city_geocoder_v2",

      "permit_type_raw": "BUILDING/EW",
      "permit_type_normalized": "alteration",
      "work_class": "alteration_minor",
      "building_subtype": "tenant_improvement",
      "is_new_construction": false,
      "is_alteration": true,
      "is_demolition": false,

      "units_residential": null,
      "units_commercial": null,
      "units_total": null,
      "square_footage_total": null,
      "valuation_usd": 18400,

      "year_built": 1925,
      "building_class": "O4",
      "zoning_district": "C6-2",
      "lot_area": 2540,

      "applied_date": "2026-05-20",
      "issued_date": "2026-05-27",
      "finaled_date": null,
      "expired_date": null,

      "current_status": "issued",
      "status_raw": "ISSUED-PARTIAL",
      "description_raw": "Interior reno @ unit 3R; partition + finishes; no MEP scope",
      "description_extracted": null,
      "description_llm_enriched": null,

      "applicant_name": null,
      "applicant_role": null,
      "contractor_name": "MIDTOWN BUILD CO INC",
      "contractor_license": "GC-0148921",
      "architect_name": null,
      "engineer_name": null,
      "owner_name": null,

      "source_url": "https://www.nyc.gov/site/buildings/permit/BT-202601090",
      "first_seen_at": "2026-05-27T03:14:22Z",
      "last_seen_at": "2026-06-04T03:11:08Z",
      "last_changed_at": "2026-05-27T03:14:22Z",

      "cohort": "commercial_alteration",
      "dq_flags": {
        "issued_date_is_parse_bug": false,
        "issued_date_is_placeholder": false,
        "issued_date_is_future_dated": false,
        "valuation_usd_is_placeholder": false,
        "valuation_usd_is_oob": false,
        "units_residential_is_placeholder": false,
        "units_total_is_placeholder": false,
        "units_residential_is_oob": false,
        "units_total_is_oob": false,
        "units_commercial_is_placeholder": false,
        "geocode_is_sentinel": false,
        "geocode_is_oob": false
      },
      "pluto": null
    }
  ],
  "pagination": {
    "total_count": 1000,
    "next_page": 2,
    "has_more": true
  }
}
```

**Note on `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`

Most fields are **nullable** — coverage varies by jurisdiction and by upstream record completeness.

### 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 to `target_segment` on `/v1/permits/target`. See [Concepts → Cohorts](/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:** `false` means *"the rule has not flagged this value,"* NOT *"the value was independently verified clean."* NULL silver flag values are coerced to `false` per the SD-039 rule. See [Data freshness → field-level DQ flags](/concepts/data-freshness#field-level-data-quality-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 from `description_extracted` (which is adapter-populated, per-jurisdiction shape). **Coverage today:** Austin + Chicago `commercial_new` permits 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:

```
GET /v1/permits/by-canonical-uid/{canonical_uid}
```

See [Get a permit by canonical\_uid](/api-reference/permits-by-canonical-uid).

Note that the **top-level PLUTO-derived fields** (`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 OK` with an empty
  `items` array — no `404`. Use `pagination.total_count` to confirm
  the empty result.
* **Invalid slug** (not in the 18-cohort enum): `422 Unprocessable Entity`
  with a problem+json body enumerating the accepted whitelist.

## Quotas

Quotas are tier-based and apply to every request against this endpoint.
See [Rate limits](/concepts/rate-limits) for the matrix + the
`X-RateLimit-*` response headers + the 429 problem+json shape.

## Example request

```bash theme={null}
curl "https://api.permitcore.io/v1/permits?jurisdiction=nyc&issued_after=2026-05-01&per_page=25" \
  -H "Authorization: Bearer $PERMITCORE_API_KEY" \
  -H "Accept: application/json"
```

## Related

* [Get a permit by canonical\_uid](/api-reference/permits-by-canonical-uid) — the per-permit detail endpoint with full enrichment + nested `pluto`.
* [Cohort distribution](/api-reference/jurisdictions/cohort-distribution) — aggregate counts behind these rows.
* [Free sandbox](https://permitcore.io/sandbox) — client-side DuckDB-WASM
  queries against the last 10 days of permits across the live metros. No
  signup, no API key.
