# CBS statistics per postcode and per grid square

CBS also publishes its key figures per postcode area (4 digits, and 6 characters) and per grid square of 100 and 500 metres, finer than the district and neighbourhood division. Each area has about 140 fields: residents by age and origin, households, dwellings by construction year and ownership, WOZ value, energy use, income, benefits, urbanisation and the distance to amenities such as GP, supermarket and station. Postcode figures run from 2015 to 2024, grid squares from 2000 to 2024. In 2024 there were 4,071 four-digit postcode areas and 464,964 six-character ones. Via PDOK as OGC API, and per year as WFS and WMS; CBS itself also offers ZIP files, which already include 2025 (first version).

- Publisher: Centraal Bureau voor de Statistiek, Publieke Dienstverlening Op de Kaart
- Area: Netherlands · Finest detail: postcode (6 characters) and 100-metre grid square
- Period: 2015 tot en met 2024 (postcode via PDOK; 2025 als CBS-download), 2000 tot en met 2024 (vierkanten) · Refreshed: yearly
- Access: Open · Licence: CC BY 4.0 (volgens de landing pages van de vier OGC API's)
- Join keys: postcode (pc4, pc6), crs28992res100m (vierkantcode, bv. E2054N6114), jaarcode
- Status: ok (2025-10-13) · 1 collectie · postcode4
- URL: https://www.cbs.nl/nl-nl/dossier/nederland-regionaal/geografische-data/gegevens-per-postcode

## What can you do with it?
- **Figures for any address list.** Almost every list has a postcode. Join on the four digits and each address gets the population, average WOZ value and distance to the GP of its area.
- **City or countryside?.** Each postcode area has an urbanisation class from 1 (extremely urbanised) to 5 (not urbanised). In 2024, 24% of residents lived in an extremely urbanised area and 14% in a non-urbanised one.
- **A 100 by 100 metre heat map.** The 100-metre squares show where people live, regardless of municipal boundaries. Compare 2000 with 2024 to see new housing estates appear.
- **Distance to amenities.** Fields such as dichtstbijzijnde_huisartsenpraktijk_afstand_in_km and the train station give the average road distance per area. Map where the station is far away.

## How to process it
1. **One postcode, one year.** The OGC API filters on postcode and jaarcode (and location). The response has 143 fields plus the boundary as geometry.
   `https://api.pdok.nl/cbs/postcode4/ogc/v1/collections/postcode4/items?f=json&postcode=3511&jaarcode=2024`
2. **One field for the whole country, without boundaries.** The OGC API always includes the geometry. The yearly WFS (service.pdok.nl/cbs/postcode4/<year>/wfs/v1_0, for 2022, 2023 and 2024) returns just the field with GetPropertyValue: 1,000 values per request, sorted by postcode.
   `https://service.pdok.nl/cbs/postcode4/2024/wfs/v1_0?service=WFS&version=2.0.0&request=GetPropertyValue&typeNames=postcode4&valueReference=aantalInwoners&sortBy=postcode&count=1000&startIndex=0`
3. **100-metre squares.** Filter on jaarcode and a bbox (in degrees). The square code is in crs28992res100m: E2054N6114 is the lower-left corner at x 205,400, y 611,400 in RD.
   `https://api.pdok.nl/cbs/vierkantstatistieken100m/ogc/v1/collections/vierkant100/items?f=json&jaarcode=2024&bbox=5.11,52.08,5.13,52.10&limit=100`
4. **Filter out the codes.** Missing figures are codes such as -99997 and -99995 (their meaning is in the CBS product notes). Set them to empty before calculating; otherwise an average turns strongly negative.

## Interesting combinations
- How many dwellings per postcode were built after 2015? → [pdok-bag](https://www.opendataindex.nl/en/bron/pdok-bag.md) via postcode (BAG-adressen hebben een postcode). Count BAG residential units per postcode and construction year and compare with aantal_woningen_bouwjaar_15_en_later from the CBS figures.
- Do more older people live where liveability is lower? → [leefbaarometer](https://www.opendataindex.nl/en/bron/leefbaarometer.md) via coördinaten (vierkant van 100 meter). Both use 100-metre squares. Join on the square and compare the age structure with the score.

## Similar sources
- [cbs-kwb](https://www.opendataindex.nl/en/bron/cbs-kwb.md): Key figures per district and neighbourhood, with more variables and a fixed division; postcode and grid are finer but more often suppressed.
- [pdok-wijkenbuurten](https://www.opendataindex.nl/en/bron/pdok-wijkenbuurten.md): The boundaries of districts and neighbourhoods with similar figures; for postcodes use this source.
- [leefbaarometer](https://www.opendataindex.nl/en/bron/leefbaarometer.md): A composite liveability score at 100 metres; this source gives the individual figures.

## What to watch out for
- **Many figures suppressed.** According to CBS, figures for residents, households and dwellings are withheld when the total in an area is below five; a code such as -99997 appears instead. For pc6 and 100-metre squares this is common: the first 2024 square has 5 residents and almost only -99997 otherwise.
- **Rounded counts.** According to CBS, counts for 2023 to 2025 are usually rounded to multiples of five. Summed over all postcodes, 2024 gives 17.9 million residents, slightly below the official population.
- **Not every field in every year.** According to the description, 2022 and 2023 were extended in 2025 with demographics, housing, energy, income and amenities; older years have fewer fields. Some 2024 fields are a code (-99995), such as the WOZ value for postcode 3511.
- **PDOK lags a year behind.** On 16 October 2026 PDOK had postcode figures up to 2024. CBS already publishes 2025 as ZIP (pc4, pc5 and pc6, version 1; erratum of 18 August 2026 for pc5 and pc6) on www.cbs.nl. New years and stricter confidentiality from 2025 make comparing years harder.
- **Postcode boundaries are derived.** According to CBS, Esri Nederland derives the postcode polygons from BAG addresses, and CBS simplified the boundaries. Compare years for the same postcode with care; grid squares do stay the same.

## API (OGC WFS 2.0.0)

- Base: https://service.pdok.nl/cbs/postcode4/2024/wfs/v1_0
- Access: No key. Fees: none; access constraints: https://creativecommons.org/licenses/by/4.0/deed.nl. (from the description)
- Formats: application/gml+xml; version=3.2, text/xml; subtype=gml/3.2.1, text/xml; subtype=gml/3.1.1, application/json; subtype=geojson, application/json
- Size: 4071 features in postcode4:postcode4 (tested)
- Filtering: OGC Filter Encoding (FES 2.0); PropertyIsEqualTo, PropertyIsNotEqualTo, PropertyIsLessThan, PropertyIsGreaterThan, PropertyIsLessThanOrEqualTo, PropertyIsGreaterThanOrEqualTo, PropertyIsLike, PropertyIsBetween, PropertyIsNull, Equals, Disjoint, Touches, Within, Overlaps, Crosses, Intersects, Contains, DWithin, Beyond, BBOX, During

### Endpoints
- `GET ?request=GetCapabilities`: service description
- `GET ?request=DescribeFeatureType`: fields of a layer (XSD)
- `GET ?request=GetFeature`: the features themselves, with geometry
- `GET ?request=ListStoredQueries`: stored queries
- `GET ?request=DescribeStoredQueries`: stored query details

### Parameters
- `typeNames`: which layer: postcode4:postcode4 (from the description)
- `outputFormat`: e.g. application/json for GeoJSON (from the description)
- `srsName`: projection, e.g. EPSG:4326 for web maps (default EPSG:28992, RD) (from the description)
- `count / startIndex`: paging (from the description)
- `filter`: FES XML filter on fields or geometry (from the description)
- `bbox`: box in the chosen projection (platform)

### Fields (138)
`geom` geometrie, `postcode` integer, `aantalInwoners` integer, `aantalMannen` integer, `aantalVrouwen` integer, `aantalInwoners0Tot15Jaar` integer, `aantalInwoners15Tot25Jaar` integer, `aantalInwoners25Tot45Jaar` integer, `aantalInwoners45Tot65Jaar` integer, `aantalInwoners65JaarEnOuder` integer, `aantalGeboorten` integer, `percentageGebNederlandHerkomstNederland` integer, `percentageGebNederlandHerkomstOverigEuropa` integer, `percentageGebNederlandHerkomstBuitenEuropa` integer, `percentageGebBuitenNederlandHerkomstEuropa` integer, `percentageGebBuitenNederlandHerkmstBuitenEuropa` integer, `aantalPartHuishoudens` integer, `aantalEenpersoonshuishoudens` integer, `aantalMeerpersoonshuishoudensZonderKind` integer, `aantalEenouderhuishoudens` integer, `aantalTweeouderhuishoudens` integer, `gemiddeldeHuishoudensgrootte` double, `aantalWoningen` integer, `aantalWoningenBouwjaarVoor1945` integer, `aantalWoningenBouwjaar45Tot65` integer, `aantalWoningenBouwjaar65Tot75` integer, `aantalWoningenBouwjaar75Tot85` integer, `aantalWoningenBouwjaar85Tot95` integer, `aantalWoningenBouwjaar95Tot05` integer, `aantalWoningenBouwjaar05Tot15` integer, `aantalWoningenBouwjaar15EnLater` integer, `aantalMeergezinsWoningen` integer, `percentageKoopwoningen` integer, `percentageHuurwoningen` integer, `aantalHuurwoningenInBezitWoningcorporaties` integer, `aantalNietBewoondeWoningen` integer, `gemiddeldeWozWaardeWoning` integer, `gemiddeldGasverbruikWoning` integer, `gemiddeldElektriciteitsverbruikWoning` integer, `gemiddeldInkomenHuishouden` double, `percentageLaagInkomenHuishouden` double, `percentageHoogInkomenHuishouden` double, `aantalPersonenMetUitkeringOnderAowlft` integer, `dichtstbijzijndeGroteSupermarktAfstandInKm` double, `groteSupermarktAantalBinnen1Km` double, `groteSupermarktAantalBinnen3Km` double, `groteSupermarktAantalBinnen5Km` double, `dichtstbijzijndeWinkelsOvDagelLevensmAfstInKm` double, `winkelsOvDagelLevensmAantalBinnen1Km` double, `winkelsOvDagelLevensmAantalBinnen3Km` double, `winkelsOvDagelLevensmAantalBinnen5Km` double, `dichtstbijzijndeWarenhuisAfstandInKm` double, `warenhuisAantalBinnen5Km` double, `warenhuisAantalBinnen10Km` double, `warenhuisAantalBinnen20Km` double, `dichtstbijzijndeCafeAfstandInKm` double, `cafeAantalBinnen1Km` double, `cafeAantalBinnen3Km` double, `cafeAantalBinnen5Km` double, `dichtstbijzijndeCafetariaAfstandInKm` double, `cafetariaAantalBinnen1Km` double, `cafetariaAantalBinnen3Km` double, `cafetariaAantalBinnen5Km` double, `dichtstbijzijndeHotelAfstandInKm` double, `hotelAantalBinnen5Km` double, `hotelAantalBinnen10Km` double, `hotelAantalBinnen20Km` double, `dichtstbijzijndeRestaurantAfstandInKm` double, `restaurantAantalBinnen1Km` double, `restaurantAantalBinnen3Km` double, `restaurantAantalBinnen5Km` double, `dichtstbijzijndeBuitenschoolseOpvangAfstandInKm` double, `buitenschoolseOpvangAantalBinnen1Km` double, `buitenschoolseOpvangAantalBinnen3Km` double, `buitenschoolseOpvangAantalBinnen5Km` double, `dichtstbijzijndeKinderdagverblijfAfstandInKm` double, `kinderdagverblijfAantalBinnen1Km` double, `kinderdagverblijfAantalBinnen3Km` double, `kinderdagverblijfAantalBinnen5Km` double, `dichtstbijzijndeBrandweerkazerneAfstandInKm` double, `dichtstbijzijndeOpritHoofdverkeerswegAfstandInKm` double, `dichtstbijzijndeOverstapstationAfstandInKm` double, `dichtstbijzijndeTreinstationAfstandInKm` double, `dichtstbijzijndeAttractieparkAfstandInKm` double, `attractieparkAantalBinnen10Km` double, `attractieparkAantalBinnen20Km` double, `attractieparkAantalBinnen50Km` double, `dichtstbijzijndeBioscoopAfstandInKm` double, `bioscoopAantalBinnen5Km` double, `bioscoopAantalBinnen10Km` double, `bioscoopAantalBinnen20Km` double, `dichtstbijzijndeMuseumAfstandInKm` double, `museumAantalBinnen5Km` double, `museumAantalBinnen10Km` double, `museumAantalBinnen20Km` double, `dichtstbijzijndeTheaterAfstandInKm` double, `theaterAantalBinnen5Km` double, `theaterAantalBinnen10Km` double, `theaterAantalBinnen20Km` double, `dichtstbijzijndeBibliotheekAfstandInKm` double, `dichtstbijzijndeKunstijsbaanAfstandInKm` double, `dichtstbijzijndePoppodiumAfstandInKm` double, `dichtstbijzijndeSaunaAfstandInKm` double, `dichtstbijzijndeZonnebankAfstandInKm` double, `dichtstbijzijndeZwembadAfstandInKm` double, `dichtstbijzijndeBasisonderwijsAfstandInKm` double, `basisonderwijsAantalBinnen1Km` double, `basisonderwijsAantalBinnen3Km` double, `basisonderwijsAantalBinnen5Km` double, `dichtstbijzijndeHavoVwoAfstandInKm` double, `havoVwoAantalBinnen3Km` double, `havoVwoAantalBinnen5Km` double, `havoVwoAantalBinnen10Km` double, `dichtstbijzijndeVmboAfstandInKm` double, `vmboAantalBinnen3Km` double, `vmboAantalBinnen5Km` double, `vmboAantalBinnen10Km` double, `dichtstbijzijndeVoortgezetOnderwijsAfstandInKm` double, `voortgezetOnderwijsAantalBinnen3Km` double, `voortgezetOnderwijsAantalBinnen5Km` double, `voortgezetOnderwijsAantalBinnen10Km` double, `dichtstbijzijndeHuisartsenpraktijkAfstandInKm` double, `huisartsenpraktijkAantalBinnen1Km` double, `huisartsenpraktijkAantalBinnen3Km` double, `huisartsenpraktijkAantalBinnen5Km` double, `dichtstbijzijndeZiekenhExclBuitenpoliAfstInKm` double, `ziekenhuisExclBuitenpoliAantalBinnen5Km` double, `ziekenhuisExclBuitenpoliAantalBinnen10Km` double, `ziekenhuisExclBuitenpoliAantalBinnen20Km` double, `dichtstbijzijndeZiekenhInclBuitenpoliAfstInKm` double, `ziekenhuisInclBuitenpoliAantalBinnen5Km` double, `ziekenhuisInclBuitenpoliAantalBinnen10Km` double, `ziekenhuisInclBuitenpoliAantalBinnen20Km` double, `dichtstbijzijndeApotheekAfstandInKm` double, `dichtstbijzijndeHuisartsenpostAfstandInKm` double, `omgevingsadressendichtheid` integer, `stedelijkheid` integer, `fuuid` string

### Limits and pitfalls
- At most 1,000 features per request (CountDefault). (from the description)
- Paging supported: count and startIndex (ImplementsResultPaging). (from the description)
- Each year is a separate WFS: …/cbs/postcode4/2022/…, 2023 and 2024 (2025 returned 404 on 16 Oct 2026). The OGC API https://api.pdok.nl/cbs/postcode4/ogc/v1 holds all years 2015 to 2024 in one collection. (tested)
- In the WFS field names are camelCase (aantalInwoners), in the OGC API snake_case (aantal_inwoners). (tested)

### Example
```
https://service.pdok.nl/cbs/postcode4/2024/wfs/v1_0?request=GetFeature&service=WFS&version=2.0.0&typeNames=postcode4:postcode4&outputFormat=application/json&srsName=EPSG:4326&count=1&propertyName=postcode,aantalInwoners,stedelijkheid,gemiddeldeWozWaardeWoning
```

## How to fetch it

### curl
```bash
curl "https://api.pdok.nl/cbs/postcode4/ogc/v1/collections/postcode4/items?f=json&postcode=1011&jaarcode=2024"
```

### python
```python
import requests

url = "https://api.pdok.nl/cbs/postcode4/ogc/v1/collections/postcode4/items"
for pc in [1011, 3511, 9711]:
    f = requests.get(url, params={"f": "json", "postcode": pc, "jaarcode": 2024}).json()["features"][0]["properties"]
    print(pc, f["aantal_inwoners"], f["stedelijkheid"], f["dichtstbijzijnde_treinstation_afstand_in_km"])
```


## Examples with real figures (1)

For each example the finding, the reference date and the call that fetches exactly these figures.

- **Residents by urbanisation of their postcode area: strongly urbanised is the largest part, 28% in 2024** (2024). Table postcode4 2024. [Example](https://www.opendataindex.nl/en/voorbeeld/cbs-postcode4-inwoners-stedelijkheid)
  - `https://service.pdok.nl/cbs/postcode4/2024/wfs/v1_0?service=WFS&version=2.0.0&request=GetPropertyValue&typeNames=postcode4&valueReference=aantalInwoners&sortBy=postcode&count=1000&startIndex=0`
  - `https://service.pdok.nl/cbs/postcode4/2024/wfs/v1_0?service=WFS&version=2.0.0&request=GetPropertyValue&typeNames=postcode4&valueReference=stedelijkheid&sortBy=postcode&count=1000&startIndex=0`
