---
title: "Distributions and summary statistics for a zoning category in a commune"
description: "Returns the per-parcel metric distributions, summary statistics, age cohorts, and raw scatter inputs that drive the `room` Aireon app. Backed by an in-memory LRU keyed on `${fso}:${cz_local}` (capacity 500, TTL 1 hour).\n\nServer-side rules:\n- Nulls are dropped before computing distributions and summary.\n- Hard outliers (above p99.5 or below p0.5 within the zone) are trimmed from `distributions[*]`; raw min/max stay in `summary`.\n- `parcels[]` is unfiltered.\n- `other_zones` lists every distinct `cz_local` in the same FSO except the queried one, sorted by parcel count desc.\n\n**GET alias (cacheable).** Identical handler and identical response to the POST, with the inputs as query parameters. A POST is uncacheable by browsers and by any CDN, so apps on a first-load path should prefer this GET.\n\nThe response carries `Cache-Control: private, max-age=86400, stale-while-revalidate=21600` and `Vary: Origin, token`. The policy is **private, never public**: the endpoint is gated on the `token` request header, and a header is not part of any shared-cache key - a `public`/`s-maxage` directive would let a CDN serve the payload to a caller that never presented the token. Error responses (including the legacy HTTP-200 plain-text ones) carry `Cache-Control: no-store`.\n\n**Snapshot and revalidation.** Every 200 carries `X-Res-Snapshot` (the identity of the serving data the payload is computed from: the serving table, plus the last completed serving refresh when recorded, e.g. `parcel_2025_07@20261020T031200Z`) and a strong `ETag` derived from the snapshot, the payload version, `fso`, `zone` and `lang` (never from the body). Send it back as `If-None-Match` and a match answers `304` before any computation runs, token required. Pass `snapshot=<value>` to pin the data version: when it is not the current snapshot the answer is `409 {\"error\":\"snapshot_mismatch\",\"snapshot\":<current>}` with `Cache-Control: no-store`."
---

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

Path: RES API (public) › Parcel

`GET /res_api/zone_stats`

Returns the per-parcel metric distributions, summary statistics, age cohorts, and raw scatter inputs that drive the `room` Aireon app. Backed by an in-memory LRU keyed on `${fso}:${cz_local}` (capacity 500, TTL 1 hour).

Server-side rules:
- Nulls are dropped before computing distributions and summary.
- Hard outliers (above p99.5 or below p0.5 within the zone) are trimmed from `distributions[*]`; raw min/max stay in `summary`.
- `parcels[]` is unfiltered.
- `other_zones` lists every distinct `cz_local` in the same FSO except the queried one, sorted by parcel count desc.

**GET alias (cacheable).** Identical handler and identical response to the POST, with the inputs as query parameters. A POST is uncacheable by browsers and by any CDN, so apps on a first-load path should prefer this GET.

The response carries `Cache-Control: private, max-age=86400, stale-while-revalidate=21600` and `Vary: Origin, token`. The policy is **private, never public**: the endpoint is gated on the `token` request header, and a header is not part of any shared-cache key - a `public`/`s-maxage` directive would let a CDN serve the payload to a caller that never presented the token. Error responses (including the legacy HTTP-200 plain-text ones) carry `Cache-Control: no-store`.

**Snapshot and revalidation.** Every 200 carries `X-Res-Snapshot` (the identity of the serving data the payload is computed from: the serving table, plus the last completed serving refresh when recorded, e.g. `parcel_2025_07@20261020T031200Z`) and a strong `ETag` derived from the snapshot, the payload version, `fso`, `zone` and `lang` (never from the body). Send it back as `If-None-Match` and a match answers `304` before any computation runs, token required. Pass `snapshot=<value>` to pin the data version: when it is not the current snapshot the answer is `409 {"error":"snapshot_mismatch","snapshot":<current>}` with `Cache-Control: no-store`.

## Authentication

- `ResApiToken`, apiKey, in header, header `token`

## Query parameters

- `fso` (`getZoneStatsGet.query.fso`; integer, optional): BFS/OFS commune number. Required.
- `zone` (`getZoneStatsGet.query.zone`; string, optional): Local construction-zone label (`cz_local`). Required (as `zone` or `cz_local`); `zone` wins when both are sent.
- `cz_local` (`getZoneStatsGet.query.cz_local`; string, optional): Alias of `zone`.
- `snapshot` (`getZoneStatsGet.query.snapshot`; string, optional): Optional pin: the `X-Res-Snapshot` value the caller expects. A different current snapshot answers 409.
- `lang` (`getZoneStatsGet.query.lang`; string, optional): Echoed back as `_lang`; does not change the data.

## Code samples

### cURL

```bash
curl --request GET \
  --url https://res.aireon.ch/res_api/zone_stats \
  --header 'token: <token>'
```

### TypeScript

```typescript
const url = 'https://res.aireon.ch/res_api/zone_stats';
const options = {method: 'GET', headers: {token: '<token>'}};

fetch(url, options)
  .then(res => res.json())
  .then(json => console.log(json))
  .catch(err => console.error(err));
```

### Python

```python
import requests

url = "https://res.aireon.ch/res_api/zone_stats"

headers = {"token": "<token>"}

response = requests.get(url, headers=headers)

print(response.text)
```

## Responses

### 200

Zone statistics payload.

#### Example

```json
{
  "zone": {
    "fso": 0,
    "municipality_name": "string",
    "cz_local": "string",
    "cz_canton": "string",
    "parcel_count": 0
  },
  "other_zones": [
    {
      "cz_local": "string",
      "parcel_count": 0
    }
  ],
  "distributions": {
    "ratio_s": [
      0
    ],
    "gfz": [
      0
    ],
    "bldg_height_m": [
      0
    ],
    "bldg_floors_n": [
      0
    ]
  },
  "summary": {
    "ratio_s": {
      "min": 0,
      "max": 0,
      "p5": 0,
      "p25": 0,
      "p50": 0,
      "p75": 0,
      "p95": 0,
      "mean": 0,
      "n": 0
    },
    "gfz": {
      "min": 0,
      "max": 0,
      "p5": 0,
      "p25": 0,
      "p50": 0,
      "p75": 0,
      "p95": 0,
      "mean": 0,
      "n": 0
    },
    "bldg_height_m": {
      "min": 0,
      "max": 0,
      "p5": 0,
      "p25": 0,
      "p50": 0,
      "p75": 0,
      "p95": 0,
      "mean": 0,
      "n": 0
    },
    "bldg_floors_n": {
      "min": 0,
      "max": 0,
      "p5": 0,
      "p25": 0,
      "p50": 0,
      "p75": 0,
      "p95": 0,
      "mean": 0,
      "n": 0
    }
  },
  "age_cohorts": {
    "now": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last60": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last40": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last20": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last15": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last10": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    },
    "last5": {
      "cohort_label": "string",
      "n": 0,
      "p50_cz_util_now": 0,
      "p80_cz_util_now": 0,
      "n_cz_util_now": 0,
      "mean_cz_util_now": 0
    }
  },
  "parcels": [
    {
      "egrid": "string",
      "area": 0,
      "volume": 0,
      "year": 0
    }
  ],
  "_lang": "string"
}
```

- `zone` (`getZoneStatsGet.response.200.zone`; object, required)
  - `fso` (`getZoneStatsGet.response.200.zone.fso`; integer, required)
  - `cz_local` (`getZoneStatsGet.response.200.zone.cz_local`; string, required)
  - `parcel_count` (`getZoneStatsGet.response.200.zone.parcel_count`; integer, required)
  - `municipality_name` (`getZoneStatsGet.response.200.zone.municipality_name`; string | null, optional)
  - `cz_canton` (`getZoneStatsGet.response.200.zone.cz_canton`; string | null, optional)
- `other_zones` (`getZoneStatsGet.response.200.other_zones`; array<object>, required): Sibling cz_local values in the same FSO, sorted by parcel count desc.
  - `cz_local` (`getZoneStatsGet.response.200.other_zones.cz_local`; string, required)
  - `parcel_count` (`getZoneStatsGet.response.200.other_zones.parcel_count`; integer, required)
- `distributions` (`getZoneStatsGet.response.200.distributions`; object, required): Trimmed (p0.5–p99.5) per-parcel samples for each metric.
  - `ratio_s` (`getZoneStatsGet.response.200.distributions.ratio_s`; array<number>, optional)
  - `gfz` (`getZoneStatsGet.response.200.distributions.gfz`; array<number>, optional)
  - `bldg_height_m` (`getZoneStatsGet.response.200.distributions.bldg_height_m`; array<number>, optional)
  - `bldg_floors_n` (`getZoneStatsGet.response.200.distributions.bldg_floors_n`; array<number>, optional)
- `summary` (`getZoneStatsGet.response.200.summary`; object, required)
  - `ratio_s` (`getZoneStatsGet.response.200.summary.ratio_s`; object, optional): Summary statistics for one metric across the zone. Computed on the raw (non-trimmed) sample; min/max are honest to the underlying data.
    - `n` (`getZoneStatsGet.response.200.summary.ratio_s.n`; integer, required)
    - `min` (`getZoneStatsGet.response.200.summary.ratio_s.min`; number | null, optional)
    - `max` (`getZoneStatsGet.response.200.summary.ratio_s.max`; number | null, optional)
    - `p5` (`getZoneStatsGet.response.200.summary.ratio_s.p5`; number | null, optional)
    - `p25` (`getZoneStatsGet.response.200.summary.ratio_s.p25`; number | null, optional)
    - `p50` (`getZoneStatsGet.response.200.summary.ratio_s.p50`; number | null, optional)
    - `p75` (`getZoneStatsGet.response.200.summary.ratio_s.p75`; number | null, optional)
    - `p95` (`getZoneStatsGet.response.200.summary.ratio_s.p95`; number | null, optional)
    - `mean` (`getZoneStatsGet.response.200.summary.ratio_s.mean`; number | null, optional)
  - `gfz` (`getZoneStatsGet.response.200.summary.gfz`; object, optional): Summary statistics for one metric across the zone. Computed on the raw (non-trimmed) sample; min/max are honest to the underlying data.
    - `n` (`getZoneStatsGet.response.200.summary.gfz.n`; integer, required)
    - `min` (`getZoneStatsGet.response.200.summary.gfz.min`; number | null, optional)
    - `max` (`getZoneStatsGet.response.200.summary.gfz.max`; number | null, optional)
    - `p5` (`getZoneStatsGet.response.200.summary.gfz.p5`; number | null, optional)
    - `p25` (`getZoneStatsGet.response.200.summary.gfz.p25`; number | null, optional)
    - `p50` (`getZoneStatsGet.response.200.summary.gfz.p50`; number | null, optional)
    - `p75` (`getZoneStatsGet.response.200.summary.gfz.p75`; number | null, optional)
    - `p95` (`getZoneStatsGet.response.200.summary.gfz.p95`; number | null, optional)
    - `mean` (`getZoneStatsGet.response.200.summary.gfz.mean`; number | null, optional)
  - `bldg_height_m` (`getZoneStatsGet.response.200.summary.bldg_height_m`; object, optional): Summary statistics for one metric across the zone. Computed on the raw (non-trimmed) sample; min/max are honest to the underlying data.
    - `n` (`getZoneStatsGet.response.200.summary.bldg_height_m.n`; integer, required)
    - `min` (`getZoneStatsGet.response.200.summary.bldg_height_m.min`; number | null, optional)
    - `max` (`getZoneStatsGet.response.200.summary.bldg_height_m.max`; number | null, optional)
    - `p5` (`getZoneStatsGet.response.200.summary.bldg_height_m.p5`; number | null, optional)
    - `p25` (`getZoneStatsGet.response.200.summary.bldg_height_m.p25`; number | null, optional)
    - `p50` (`getZoneStatsGet.response.200.summary.bldg_height_m.p50`; number | null, optional)
    - `p75` (`getZoneStatsGet.response.200.summary.bldg_height_m.p75`; number | null, optional)
    - `p95` (`getZoneStatsGet.response.200.summary.bldg_height_m.p95`; number | null, optional)
    - `mean` (`getZoneStatsGet.response.200.summary.bldg_height_m.mean`; number | null, optional)
  - `bldg_floors_n` (`getZoneStatsGet.response.200.summary.bldg_floors_n`; object, optional): Summary statistics for one metric across the zone. Computed on the raw (non-trimmed) sample; min/max are honest to the underlying data.
    - `n` (`getZoneStatsGet.response.200.summary.bldg_floors_n.n`; integer, required)
    - `min` (`getZoneStatsGet.response.200.summary.bldg_floors_n.min`; number | null, optional)
    - `max` (`getZoneStatsGet.response.200.summary.bldg_floors_n.max`; number | null, optional)
    - `p5` (`getZoneStatsGet.response.200.summary.bldg_floors_n.p5`; number | null, optional)
    - `p25` (`getZoneStatsGet.response.200.summary.bldg_floors_n.p25`; number | null, optional)
    - `p50` (`getZoneStatsGet.response.200.summary.bldg_floors_n.p50`; number | null, optional)
    - `p75` (`getZoneStatsGet.response.200.summary.bldg_floors_n.p75`; number | null, optional)
    - `p95` (`getZoneStatsGet.response.200.summary.bldg_floors_n.p95`; number | null, optional)
    - `mean` (`getZoneStatsGet.response.200.summary.bldg_floors_n.mean`; number | null, optional)
- `age_cohorts` (`getZoneStatsGet.response.200.age_cohorts`; object, required): Mean, p50 and p80 of cz_util_now (utilization) per building-age cohort, widest window first. Seven precision steps: the all-years baseline plus six trailing windows (60/40/20/15/10/5 years). The 15/10/5 steps were added 2026-08-24. Windows are relative to the current year. Each cohort carries `n` - the narrower the window the thinner the sample, so read `n` before trusting a cohort mean.
  - `now` (`getZoneStatsGet.response.200.age_cohorts.now`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.now.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.now.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.now.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.now.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.now.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.now.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last60` (`getZoneStatsGet.response.200.age_cohorts.last60`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last60.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last60.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last60.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last60.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last60.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last60.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last40` (`getZoneStatsGet.response.200.age_cohorts.last40`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last40.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last40.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last40.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last40.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last40.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last40.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last20` (`getZoneStatsGet.response.200.age_cohorts.last20`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last20.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last20.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last20.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last20.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last20.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last20.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last15` (`getZoneStatsGet.response.200.age_cohorts.last15`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last15.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last15.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last15.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last15.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last15.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last15.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last10` (`getZoneStatsGet.response.200.age_cohorts.last10`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last10.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last10.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last10.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last10.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last10.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last10.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
  - `last5` (`getZoneStatsGet.response.200.age_cohorts.last5`; object, optional): One age cohort of the zone, on utilization: the mean, p50 and p80 of cz_util_now (gfa_now / parcel_area, the floor-area utilization) over the same parcels, with n_cz_util_now as their sample size. mean_cz_util_now was added in v0.0.323. The deprecated mean_ratio_v (not utilization) was removed in v0.0.324.
    - `cohort_label` (`getZoneStatsGet.response.200.age_cohorts.last5.cohort_label`; string, required)
    - `n` (`getZoneStatsGet.response.200.age_cohorts.last5.n`; integer, required)
    - `p50_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last5.p50_cz_util_now`; number | null, optional): Median cz_util_now (gfa_now / parcel_area) of the cohort, percentile_cont(0.5). Null when n_cz_util_now is 0.
    - `p80_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last5.p80_cz_util_now`; number | null, optional): 80th percentile of cz_util_now of the cohort, percentile_cont(0.8). Null when n_cz_util_now is 0.
    - `n_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last5.n_cz_util_now`; integer, optional): Parcels in the cohort with a usable cz_util_now (the sample behind the mean and the two percentiles).
    - `mean_cz_util_now` (`getZoneStatsGet.response.200.age_cohorts.last5.mean_cz_util_now`; number | null, optional): Mean cz_util_now (gfa_now / parcel_area) of the cohort, over the same rows as the percentiles (weight: n_cz_util_now). Null when n_cz_util_now is 0.
- `parcels` (`getZoneStatsGet.response.200.parcels`; array<object>, required): Unfiltered per-parcel scatter input - egrid, parcel area, built volume, latest construction year.
  - `egrid` (`getZoneStatsGet.response.200.parcels.egrid`; string, optional)
  - `area` (`getZoneStatsGet.response.200.parcels.area`; number | null, optional)
  - `volume` (`getZoneStatsGet.response.200.parcels.volume`; number | null, optional)
  - `year` (`getZoneStatsGet.response.200.parcels.year`; integer | null, optional)
- `_lang` (`getZoneStatsGet.response.200._lang`; string | null, optional)

### 304

`If-None-Match` matches the current `ETag`. Empty body; answered before the payload is computed.

### 400

Missing or invalid `fso` / `cz_local`.

#### Example

```json
{
  "error": "string",
  "message": "string"
}
```

- `error` (`getZoneStatsGet.response.400.error`; string, required): Human-readable error description.
- `message` (`getZoneStatsGet.response.400.message`; string, optional): Optional underlying error message.

### 401

Missing token.

#### Example

```json
{
  "error": "string",
  "message": "string"
}
```

- `error` (`getZoneStatsGet.response.401.error`; string, required): Human-readable error description.
- `message` (`getZoneStatsGet.response.401.message`; string, optional): Optional underlying error message.

### 403

Wrong token.

#### Example

```json
{
  "error": "string",
  "message": "string"
}
```

- `error` (`getZoneStatsGet.response.403.error`; string, required): Human-readable error description.
- `message` (`getZoneStatsGet.response.403.message`; string, optional): Optional underlying error message.

### 409

`snapshot` was given and is not the current snapshot. `Cache-Control: no-store`.

#### Example

```json
{
  "error": "snapshot_mismatch",
  "snapshot": "string"
}
```

- `error` (`getZoneStatsGet.response.409.error`; string, optional)
  - example `"snapshot_mismatch"`
- `snapshot` (`getZoneStatsGet.response.409.snapshot`; string, optional): The current snapshot.

### 500

Server error.

#### Example

```json
{
  "error": "string",
  "message": "string"
}
```

- `error` (`getZoneStatsGet.response.500.error`; string, required): Human-readable error description.
- `message` (`getZoneStatsGet.response.500.message`; string, optional): Optional underlying error message.


Source: https://docs.aireon.ch/res/get-zone-stats-get/index.md
