# Samen Meten (RIVM)

Burgers, gemeenten en onderzoekers hangen zelf sensorkastjes op; het RIVM verzamelt hun metingen in het Samen Meten-dataportaal en maakt voor fijnstof gekalibreerde reeksen. Op 16 oktober 2026 stonden er 12.419 meetpunten in de API, met 56.391 meetreeksen: fijnstof (PM10, PM2,5), stikstofdioxide, ammoniak, temperatuur, luchtvochtigheid en luchtdruk, per uur. De grootste groep is Luftdaten (6.099 meetpunten).

- Uitgever: Rijksinstituut voor Volksgezondheid en Milieu
- Gebied: Nederland · Laagste detail: meetpunt (sensorkastje)
- Periode: actueel, met historie per meetpunt · Ververst: elk uur
- Toegang: Open · Licentie: niet vastgesteld ("Onze data zijn Open Data", zonder licentienaam)
- Koppelsleutels: Thing-naam (SMC_20058), codegemeente, knmicode, dichtstbijzijnde Luchtmeetnet-station (no2closecode, pm25closecode …)
- Status: ok (2026-10-16) · PM2,5 6,61 ug/m3 (SMC_20058)
- URL: https://www.samenmeten.nl/dataportaal/api-application-programming-interface

## Wat kun je ermee?
- **Hoe vies is de lucht in mijn straat?.** Zoek de meetpunten rond een adres met geo.distance en haal de gekalibreerde fijnstofreeks (pm25_kal) op. Rond de Utrechtse Dom staan 10 meetpunten binnen 0,005 graad.
- **Wie meet er mee?.** Elk meetpunt hoort bij een project: Luftdaten (6.099), Palmes (1.837), Luchtclub (704), Hollandse Luchten (573). De grafiek op deze pagina telt ze.
- **Burgersensor naast het officiële station.** Elk meetpunt noemt het dichtstbijzijnde Luchtmeetnet-station (bv. no2closecode NL10131). Zet de twee reeksen naast elkaar en kijk hoe goed de kalibratie werkt.
- **Vuurwerk en houtstook zichtbaar maken.** Er zijn projecten met de namen Vuurwerk (75 meetpunten) en Houtstook (28). Bekijk de fijnstofpieken rond oud en nieuw of op koude winteravonden.

## Zo verwerk je het
1. **Meetpunten van één project.** Things zijn de meetpunten. Filter op properties/project; $count=true geeft het aantal, $top hoogstens 200 per pagina (volg @iot.nextLink).
   `https://api-samenmeten.rivm.nl/v1.0/Things?$filter=properties/project%20eq%20'Smartcitizen'&$count=true&$top=5`
2. **De laatste metingen van een reeks.** Elke reeks (Datastream) heet <meetpunt>-<nummer>-<grootheid>. Met $expand haal je de laatste metingen in dezelfde aanroep op.
   `https://api-samenmeten.rivm.nl/v1.0/Datastreams?$filter=name%20eq%20'SMC_20058-21-pm25_kal'&$expand=Observations($top=2;$orderby=phenomenonTime%20desc)`
3. **Meetpunten rond een plek.** geo.distance werkt alleen met SRID=4326 erbij in de geografie. De coördinaten in de antwoorden zijn afgerond op drie decimalen (ongeveer 100 meter).
   `https://api-samenmeten.rivm.nl/v1.0/Locations?$filter=geo.distance(location,geography'SRID=4326;POINT(5.121314%2052.090654)')%20le%200.005&$expand=Things($select=name)`
4. **Welke grootheden er zijn.** Negen: pres, rh, temp, nh3, no2, pm25_kal, pm10_kal, pm25 en pm10. Gebruik voor fijnstof de gekalibreerde (_kal).
   `https://api-samenmeten.rivm.nl/v1.0/ObservedProperties`

## Interessante koppelingen
- Klopt de burgersensor? → [luchtmeetnet](https://www.opendataindex.nl/bron/luchtmeetnet.md) via no2closecode / pm25closecode (stationscode, bv. NL10131). Elk meetpunt noemt zijn dichtstbijzijnde, regionale en stadsstation. Haal van dat station dezelfde uren op bij Luchtmeetnet en vergelijk.
- Meer fijnstof bij weinig wind? → [knmi-daggegevens](https://www.opendataindex.nl/bron/knmi-daggegevens.md) via knmicode (knmi_06375 = station 375). Het KNMI-station staat bij elk meetpunt. Zet de dagelijkse windsnelheid naast het daggemiddelde fijnstof.

## Vergelijkbare bronnen
- [luchtmeetnet](https://www.opendataindex.nl/bron/luchtmeetnet.md): Het officiële meetnet van RIVM, GGD's en provincies; veel minder meetpunten, maar met referentie-apparatuur.
- [rivm-gcn](https://www.opendataindex.nl/bron/rivm-gcn.md): Berekende jaargemiddelden op een kaart van heel Nederland, geen metingen per uur.
- [eea-lucht](https://www.opendataindex.nl/bron/eea-lucht.md): Officiële metingen van heel Europa, vergelijkbaar tussen landen.

## Waar je op moet letten
- **Veel meetpunten meten niet meer.** De API telt alle meetpunten die ooit zijn aangemeld. Een Palmes-meetpunt dat we bekeken had geen enkele meting. Kijk per reeks naar de laatste meting.
- **Ruwe sensorwaarden zijn onbetrouwbaar.** Goedkope fijnstofsensoren kunnen flink afwijken, bijvoorbeeld bij hoge luchtvochtigheid. Gebruik de gekalibreerde reeksen (pm25_kal, pm10_kal), niet pm25 en pm10.
- **Brede vragen lopen vast.** Observations zonder filter, of een $expand over veel meetpunten tegelijk, gaf een 504 (Gateway Time-out). Een filter op Observations/phenomenonTime vanuit Datastreams gaf een lege reactie. Werk per reeks.
- **Licentie niet benoemd.** Samen Meten schrijft "Onze data zijn Open Data", zonder licentienaam. Een oudere dataset van Utrecht op data.overheid.nl staat onder CC0, maar voor het RIVM-portaal is dat niet vastgesteld.
- **Locaties afgerond.** Coördinaten staan op drie decimalen (ongeveer 100 meter). Genoeg voor een buurt, niet voor een huisnummer.

## API (OGC SensorThings API 1.0 (OData-achtig, JSON))

- Basis: https://api-samenmeten.rivm.nl/v1.0
- Toegang: Geen sleutel; de API is openbaar (getest). (getest)
- Formaten: json
- Omvang: 12425 Things (sensorkastjes) met 56.418 datastreams, 24.613 locaties en 52 sensortypen ($count, 16 okt 2026) (getest)
- Filteren: OData-achtige expressies in $filter (SensorThings), ook op properties/… en met geo.distance(location, geography'SRID=4326;POINT(lon lat)') le <graden>.; eq, ne, gt, ge, lt, le, and, or, startswith(), geo.distance()

### Endpoints
- `GET /Things`: Sensorkastjes; properties met project, owner, gemeentecode en de dichtstbijzijnde officiële stations per stof (no2closecode, pm25regiocode …).
- `GET /Datastreams`: Eén meetreeks per kastje en grootheid, naam <kastje>-<nr>-<grootheid> (SMC_20058-21-pm25_kal), met eenheid.
- `GET /Datastreams({id})/Observations`: Uurwaarden van één reeks (phenomenonTime, result).
- `GET /Observations`: Alle waarnemingen; altijd filteren, de hele set is te groot om te tellen.
- `GET /ObservedProperties`: De 9 grootheden: pm10, pm25, pm10_kal, pm25_kal (gekalibreerd), no2, nh3, temp, rh, pres.
- `GET /Sensors`: De 52 sensoren op naam (Sensirion SEN5x, AirVisual …); het nummer in description komt terug in de datastreamnaam.
- `GET /Locations`: Plaatsen van de kastjes (GeoJSON-punt); zoek in de buurt met geo.distance.
- `GET /HistoricalLocations`: Eerdere plaatsen van een kastje (verhuisd).
- `GET /FeaturesOfInterest`: Waar een waarneming over gaat (meestal de locatie).

### Parameters
- `$filter`: Filter, bv. name eq 'SMC_20058-21-pm25_kal', properties/project eq 'Smartcitizen', phenomenonTime ge 2026-10-16T00:00:00Z, startswith(name,'ZWL'). (getest)
- `$expand`: Gerelateerde entiteiten meenemen, ook met eigen opties: Observations($top=2;$orderby=phenomenonTime desc). (getest)
- `$select`: Alleen deze velden (phenomenonTime,result). (getest)
- `$orderby`: Sorteren. (getest)
- `$top / $skip`: Pagineren; $top hoogstens 200 (meer wordt stil 200). (getest)
- `$count`: Totaal in @iot.count. (getest)

### Velden in het antwoord (selectie) (8)
`@iot.id` integer, `name` tekst, `properties.project / owner` tekst, `properties.codegemeente` tekst, `unitOfMeasurement.symbol` tekst, `phenomenonTime` datetime, `result` getal, `location.coordinates` punt

### Limieten en valkuilen
- $top hoogstens 200 per pagina; volg @iot.nextLink voor de rest. (getest)
- Tellen over alle Observations of alle actieve kastjes liep vast (HTTP 504). Tel per reeks of per project. (ervaring)
- Voor fijnstof bestaan twee reeksen per kastje: ruw (pm25, pm10) en gekalibreerd (pm25_kal, pm10_kal, 'fijnstof gekalibreerd' in ObservedProperties). Vergelijk met officiële metingen alleen de _kal-reeksen. (getest)
- geo.distance werkt alleen met geography'SRID=4326;POINT(lon lat)', afstand in graden (0,005 ≈ 350 m oost-west). (ervaring)
- Een fout in $filter geeft 400 met de ruwe databasefout ('pq: column "bogus" does not exist'): handig om veldnamen te controleren. (getest)
- Elk kastje noemt in properties de dichtstbijzijnde officiële Luchtmeetnet-stations per stof: zo vergelijk je direct met de referentiemeting. (getest)

### Voorbeeld
```
https://api-samenmeten.rivm.nl/v1.0/Datastreams?$filter=name%20eq%20'SMC_20058-21-pm25_kal'&$expand=Observations($top=3;$orderby=phenomenonTime%20desc)
```

## Zo haal je het op

### curl
```bash
curl "https://api-samenmeten.rivm.nl/v1.0/Datastreams?\$filter=name%20eq%20'SMC_20058-21-pm25_kal'&\$expand=Observations(\$top=24;\$orderby=phenomenonTime%20desc)"
```

### python
```python
import requests

basis = "https://api-samenmeten.rivm.nl/v1.0"
r = requests.get(f"{basis}/Datastreams", params={
    "$filter": "name eq 'SMC_20058-21-pm25_kal'",
    "$expand": "Observations($top=24;$orderby=phenomenonTime desc)"}).json()
for o in r["value"][0]["Observations"]:
    print(o["phenomenonTime"], o["result"], "µg/m³")
```

## Wat je ermee kunt bouwen
- [Luchtkwaliteit-alert voor je eigen straat](https://www.opendataindex.nl/bouwen/luchtkwaliteit-alert.md): Een melding op je telefoon als de lucht bij jou thuis slecht is of slecht wordt, op basis van de verwachting van Luchtmeetnet, de meetstations in je stad, sensoren van buren en de RIVM-jaarkaart.


## Voorbeelden met echte cijfers (1)

Per voorbeeld de bevinding, de peildatum en de aanroep die precies deze cijfers ophaalt.

- **Meetpunten in Samen Meten per project: het meest Luftdaten (6.103 meetpunten), dan Palmes en Luchtclub** (oktober 2026). Tabel SensorThings Things. [Voorbeeld](https://www.opendataindex.nl/voorbeeld/samenmeten-meetpunten-per-project)
  - `https://api-samenmeten.rivm.nl/v1.0/Things?$filter=properties/project%20eq%20'Smartcitizen'&$count=true&$top=0`
  - `https://api-samenmeten.rivm.nl/v1.0/Things?$top=200&$select=properties&$count=true`
