# CRE Layer property API

> Official county facts for commercial parcels, with exact coverage, per-field
> availability, and provenance back to the source file, row and column.

## Rules for answering from this API

1. Call `GET /counties` first. It is the authority on which counties are covered,
   how many parcels are served, the dataset date, and which fields each county
   publishes. Repeat its `caveats` to the user when they matter.
2. Served parcels are those the county's reviewed use-code mapping classifies as
   commercial. An empty result never proves a property does not exist.
3. Served facts are county-reported: situs; a coordinate point when published
   (never a polygon); the normalized asset type overlay; land area; structures
   with stories, use code and description, gross/net building area, year built
   and unit count; assessment land, improvement and total; sales with their
   qualification; and the basic zoning code and description when published.
   Money is integer USD cents; areas are square feet.
4. Separate facts from computed totals. Structure count, summed gross/net
   building area and summed unit count are deterministic and complete only when
   every contributing structure reports the field; `yearBuilt` filters match when
   any structure reports a year in range. Assessment totals are county-reported
   figures, not computed sums and not asking prices or appraisals.
5. This API serves no valuations, rent or income estimates, comparables, detailed
   zoning rules or overlays, permitted uses, entitlements, permits, hazards,
   transit or monitoring. Basic zoning is a county fact, not an entitlement
   analysis; never present a total as an appraisal.
6. Field availability is per county (`published`, `partial`, `not_published`), and
   a property lists its unpublished fields in `missingFields`. Missing or empty
   data never proves a property does not exist.
7. A query response lists every requested county with `evaluated`,
   `not_evaluable` (the county does not publish a filtered field), or
   `not_covered`. `not_evaluable` is not zero matches; report counties that could
   not be evaluated.
8. Page with `nextCursor` and identical counties, filters and order. A cursor is
   refused with `cursor_expired` when a county published a new dataset; restart.
9. No owner, taxpayer, mailing, tenant or party information exists in this API.
10. `GET /counties/{countyId}/properties/{parcelId}/context` returns two
    point-context modules for one served parcel at its published coordinate,
    with `property:read`. NAIP is catalog metadata only: not imagery, image
    bytes, asset URLs, current property condition or property-condition
    evidence. FEMA NFHL describes only the queried point: not parcel-wide
    flood risk, insurance, safety, survey, engineering or an official map
    determination, and an empty result does not mean no flood risk. Each
    module is `observed`, `not_found` (a valid empty source response),
    `not_evaluable` (for example coordinates are not published or the
    geography is not authorized) or `unavailable` (an upstream transport or
    schema failure with a safe reason code); name the state and never present
    a module as complete when it is not. Upstream module failures still return
    HTTP 200 with that module `unavailable`; only the county serving index
    being unavailable or a total route timeout is 503 `data_unavailable`.
    An unknown county is 404 `county_not_covered`, and a missing parcel or an
    out-of-scope parcel is 404 `not_found`.
11. Context `freshness.generatedAt` is when the snapshot was evaluated and
    `freshness.dataAsOf` is the last-good release date, while each module
    carries its own `provenance.retrievedAt`; `freshness.cache` is an
    operational cache-control value, not a data-freshness claim. Missing
    context never proves a parcel has no flood or imagery facts. A snapshot
    containing an `unavailable` module is served but not cached, so a later
    request can recover when the source does.
12. Every `POST /properties:query` result row and every property detail response
    embeds a required `context` bound to that row's own county, parcel, dataset
    and stored coordinate; query responses also carry a required `contextSummary`.
    `context.status` is `available` or `unavailable`. Unavailable reasons are
    `county_not_covered`, `data_unavailable`, `not_found`, `out_of_scope`,
    `timeout`, `batch_row_limit`, `batch_budget_exceeded` and `enrichment_error`.
    Context never drops, fails or reorders a row; a row keeps all its facts when
    context is unavailable. A missing coordinate is `available` with `point: null`
    and both modules `not_evaluable` with reason `coordinates_not_published`, and
    is never geocoded. Query enrichment is bounded: by default the first 25 rows
    are attempted with concurrency 4 and a 5000 ms budget, and later rows keep
    their facts with reason `batch_row_limit` or `batch_budget_exceeded`. Name the
    row's context state instead of implying the modules are complete.

## Authentication

Send `Authorization: Bearer <api-key>` on every request. Query-string keys
are rejected, and a key only works in the environment that issued it.

This deployment allows self-service account registration: create an account on the product site's account page, then create and revoke your own personal API keys there. Keys are shown once; store the key in a local secret file or environment variable, never in assistant messages, URLs, browser code or source control.

Requests are rate limited per key per minute; a 429 carries `retry-after`
seconds.

## Endpoints

- GET /counties — coverage, dataset, field availability and caveats per county
- GET /counties/{countyId} — the same for one county
- POST /properties:query — structured filters (asset type, situs city or ZIP,
  county use codes, land and building areas, year built, units, county total
  assessment, last qualified sale, text) with deterministic order, exact totals
  and dataset-pinned cursors; every result row embeds a property-scoped
  `context` and the response carries `contextSummary`; compact summary rows
  carry the most recent qualified sale
- POST /properties:resolve — a parcel identifier or an address to one served parcel,
  or an honest ambiguous / out_of_scope / not_found answer
- GET /counties/{countyId}/properties/{parcelId} — full facts for one parcel,
  including structures, every sale with its qualification and basic zoning when
  published, the fields the county did not publish for it, and an embedded
  property-scoped `context`
- GET /counties/{countyId}/properties/{parcelId}/provenance — every served field
  traced to its source artifact, row and raw value
- GET /counties/{countyId}/properties/{parcelId}/context — optional single-parcel
  route serving the same point-context modules at the parcel's published
  coordinate: USGS NAIP catalog metadata and FEMA NFHL zone and panel facts,
  each with observation or effective dates, units, method and provenance;
  `property:read`
- GET /openapi.json — the machine-readable contract; GET /docs — its viewer
- GET /llms-full.txt or /reference.md — the complete generated API contract

## Covered counties

- us-fl-broward — Broward County, FL
- us-fl-miami-dade — Miami-Dade County, FL
- us-fl-palm-beach — Palm Beach County, FL
- us-wa-king — King County, WA
- us-wa-pierce — Pierce County, WA
- us-wa-snohomish — Snohomish County, WA

Contract 2026-09-13; environment prod; release app-20261028T032509Z-e0582972e6d2.
Base URL: https://crelayer.com/data
