---
title: "Air quality for one postal code area"
method: GET
path: "/api/v1/postal-codes/{postalCode}/air-quality"
tags: ["Air quality"]
---

# Air quality for one postal code area

`GET /api/v1/postal-codes/{postalCode}/air-quality`

## Request

- Base URL: `https://suomiatlas.com`
- URL: `https://suomiatlas.com/api/v1/postal-codes/{postalCode}/air-quality`
- Auth: one of:
  - none
  - HTTP bearer

PM2.5, PM10, NO2 and O3 from the nearest FMI measuring station, with WHO and EU benchmarks and the station's identity and distance.

All four pollutant blocks are always present. **Per block, exactly one of `data` or `gap` is non-null**: `data` when a station is close enough, `gap` when none is (`reason` is `TOO_FAR` or `NO_STATION`; the threshold is 15 km for NO2 and PM10, 30 km otherwise). When `data` is present so is `provenance`. For O3 the whole `benchmarks` object is null — not its individual numbers.

**Free-tier limit:** without Pro or an Area Pass for this exact area, every `data.series` is an **empty array** while `data.latest`, the benchmarks and the station provenance stay, and `locked` names `pollutants[].data.series` (`requires: area-pass`). So the current reading is free and the history is not. An empty `series` with a `locked` entry means withheld; without one it is genuinely empty.

Example request: https://suomiatlas.com/api/v1/postal-codes/00120/air-quality

## Path parameters

- `postalCode` string, required

## Response `200`

Air quality, series possibly emptied.

- AirQuality
  - `areaCode` string, nullable, required: The postal code asked for; null on the municipality endpoint.
  - `kuntaCode` string, required
  - `kuntaName` string, required
  - `pollutants` PollutantBlock[], required: All four pollutants, always present: aq_pm25_avg, aq_pm10_avg, aq_no2_avg, aq_o3_avg.
    - `code` 'aq_pm25_avg' | 'aq_pm10_avg' | 'aq_no2_avg' | 'aq_o3_avg', required
    - `unit` string, required
    - `benchmarks` AirQualityBenchmarks, nullable, required: Reference limits for this pollutant, for saying whether a reading is high.
      - `who2021` number, double, required
      - `euLimit` number, double, required
      - `eu2030` number, double, required
    - `data` AirQualityData, nullable, required
      - `latest` AirQualityYearValue, required
        - `year` integer, required
        - `value` number, double, required
      - `series` AirQualityYearValue[], required: **Empty without Pro or an Area Pass for this area** — withheld, not unmeasured, and the response's `locked` names `pollutants[].data.series` when that happened. An empty `series` with no `locked` entry is genuinely empty. `latest` stays populated either way.
        - `year` integer, required
        - `value` number, double, required
    - `provenance` StationProvenance, nullable, required: Which station the reading came from. Quote the distance: a reading from 25 km away describes the region, not the street.
      - `fmisid` integer, required: The FMI station identifier.
      - `name` string, required
      - `distanceKm` number, double, required
      - `tier` 'local' | 'regional', required
    - `kuntaProvenance` KuntaProvenance, nullable, required
      - `stations` StationRef[], required
        - `fmisid` integer, required
        - `name` string, required
      - `stationCount` integer, required
    - `gap` AirQualityGap, nullable, required: Why this pollutant has no reading. Present exactly when `data` is null — and it means no station is close enough, never that the air is clean.
      - `reason` 'TOO_FAR' | 'NO_STATION', required: `TOO_FAR`: a station exists but is beyond the threshold. `NO_STATION`: none measures this pollutant at all.
      - `nearestStation` string, nullable, required: Null when `reason` is NO_STATION.
      - `distanceKm` number, double, nullable, required
      - `maxKm` number, double, nullable, required: The threshold applied: 15 km for NO2 and PM10, 30 km otherwise.
  - `locked` LockedField[]: Absent — the key is missing, not an empty array — when nothing was withheld for this caller. May name: `pollutants[].data.series` (`area-pass` on the postal endpoint, `pro` on the municipality endpoint).
    - `field` string, required: Path into this response, e.g. `entries`, `years`, `series.housing_*`, `pollutants[].data.series`, `composites[].components[].percentile`.
    - `requires` 'pro' | 'area-pass', required: `pro`: only a Pro subscription lifts it. `area-pass`: Pro, or an Area Pass for this exact postal code.
    - `reason` string, required: One sentence naming what is withheld. Always ends "The data exists — it is withheld, not missing."
    - `upgrade` string, uri, required

## Other responses

- `404`: No such place, or no data for it. Also what a non-numeric `year` produces, because query-parameter conversion fails before the operation runs.

---

[API](https://skmtc.dev/suomiatlas/apis/suomiatlas-read-api.md) · [All operations](https://skmtc.dev/suomiatlas/apis/suomiatlas-read-api/llms.txt) · [OpenAPI document](https://skmtc.dev/suomiatlas/apis/suomiatlas-read-api/revisions/b2627414938d?raw)
