# Key Register of the Subsurface (BRO)

The national register of what lies underground and what is measured there: cone penetration tests, geological, soil and geotechnical boreholes, groundwater monitoring wells with their time series (some from the 1950s), groundwater composition, abstractions and mining permits. Government bodies must have their surveys registered here; TNO runs the national facility on behalf of the Ministry of the Interior (BZK). Fourteen REST services on publiek.broservices.nl, each with an OpenAPI description, plus a copy on PDOK. On 16 October 2026 it held 97,768 groundwater monitoring wells.

- Publisher: TNO Geologische Dienst Nederland, Ministerie van Binnenlandse Zaken en Koninkrijksrelaties, Publieke Dienstverlening Op de Kaart
- Area: Netherlands · Finest detail: survey point (test, borehole, well)
- Period: doorlopend; meetreeksen soms vanaf de jaren 1950 · Refreshed: weekly
- Access: Open · Licence: CC0 1.0 (volgens PDOK en de TNO-registraties op data.overheid.nl)
- Join keys: broId (bv. GMW000000004076, CPT000000245346), coördinaten, KvK-nummer bronhouder
- Status: ok (2026-10-15) · 7 collecties · gm_gmw
- URL: https://www.broloket.nl

## What can you do with it?
- **How high is the groundwater under your house?.** Find the nearest groundwater well, fetch the groundwater level series as CSV and chart it. Well GMW000000004076 has measurements from 1956.
- **What lies beneath the building site?.** Within 3 kilometres of central Utrecht, 52 cone penetration tests and 59 groundwater wells have been registered since 1 January 2025. Each test shows the layers down to tens of metres.
- **Drought in the series.** Compare the groundwater level in the summers of 2018, 2022 and 2025 with the long-term average of the same well. Put KNMI rainfall next to it.
- **Who pumps up groundwater?.** Groundwater usage systems (GUF) and production files (GPD) describe abstractions and how much was pumped per period.

## How to process it
1. **Search an area.** Per object type, a POST to characteristics/searches with a JSON body: area (circle in km or box) and registration period. The response is XML with numberOfDocuments and the key data per object. More than 2,000 objects is refused: make the area or period smaller.
   `curl -X POST -H 'Content-Type: application/json' \
  -d '{"registrationPeriod":{"beginDate":"2025-01-01","endDate":"2026-10-15"},"area":{"enclosingCircle":{"center":{"lat":52.0907,"lon":5.1214},"radius":3}}}' \
  https://publiek.broservices.nl/sr/cpt/v1/characteristics/searches`
2. **From well to time series.** gmw-relations gives the tubes of a well and the related groundwater level surveys (GLD). seriesAsCsv returns the series; time is in epoch milliseconds with one column per quality regime (preliminary, assessed, control, unknown).
   `https://publiek.broservices.nl/gm/gld/v1/seriesAsCsv/GLD000000002796`
3. **The OpenAPI per service.** Each service has its description at <base>/openapi (OpenAPI 3.1, as YAML) and a Swagger page at <base>/swagger-ui. Paths: /gm/{frd,gar,gld,gmn,gmw}/v1, /sr/bhrp/v2, /sr/bhrg/v3, /sr/bhrgt/v2, /sr/cpt/v1, /sr/sfr/v2, /gu/{gpd,guf}/v1, /ep/{epc,epl}/v1.
   `https://publiek.broservices.nl/gm/gld/v1/openapi`
4. **All wells at once (PDOK).** For overviews the PDOK copy is easier: an OGC API with wells, tubes, networks and surveys, in GeoJSON, 1,000 per page (98 pages for all wells). Each object links to the XML and CSV series on publiek.broservices.nl.
   `https://api.pdok.nl/tno/bro-grondwatermonitoring-in-samenhang-karakteristieken/ogc/v1/collections/gm_gld/items?f=json&limit=10`

## Interesting combinations
- How quickly does groundwater respond to rain? → [knmi-daggegevens](https://www.opendataindex.nl/en/bron/knmi-daggegevens.md) via datum en dichtstbijzijnd station. Put a well's groundwater level next to the daily rainfall at the nearest KNMI station and see how many days it takes for the level to rise.
- How deep is the groundwater below ground level? → [ahn](https://www.opendataindex.nl/en/bron/ahn.md) via coördinaten. The BRO gives the level relative to NAP. Subtract it from the AHN ground level at the well's location.

## Similar sources
- [bodemloket](https://www.opendataindex.nl/en/bron/bodemloket.md): Soil contamination and remediation per site; the BRO covers the structure of the subsurface and groundwater.
- [ahn](https://www.opendataindex.nl/en/bron/ahn.md): The height of the ground surface; the BRO describes what lies beneath.
- [DINOloket (TNO)](https://www.dinoloket.nl): TNO's older archive of boreholes and groundwater data from before the BRO; much of it has been moved to the BRO.

## What to watch out for
- **At most 2,000 objects per search.** According to the interface description, the BRO limits the list to 2,000 documents; above that the service refuses ("Het aantal objecten is groter dan 2000"). For the whole country in one year you have to search in pieces, or use the PDOK copy.
- **XML and POST.** Searching only works via POST with a JSON body, and responses are XML in the BRO model. Only the series are available as CSV.
- **No totals.** The REST services give no national counts, and the list of bro-ids requires a data owner's KvK number. For counts, fetch the full PDOK copy.
- **Old and new mixed.** Wells from before the BRO were migrated from older archives (quality regime IMBRO/A, 72,798 of 97,768 wells); they are less complete than new registrations (IMBRO). 11,985 wells have since been removed but are still listed.

## API (REST (OpenAPI 3.1 als YAML; zoeken met POST + JSON, antwoord XML of CSV))

- Base: https://publiek.broservices.nl
- Access: No key or account needed (tested)
- Formats: xml, csv, json
- Size: 97768 groundwater monitoring wells (PDOK copy, 16 Oct 2026; 11,985 of them removed) (tested)
- Filtering: No query language: a JSON body (criteria set) with area, periods and a few attributes. Then continue per bro-id.; 

### Endpoints
- `POST /{domein}/{type}/{versie}/characteristics/searches`: Search an area (circle in km or box in WGS84) and period; XML response with numberOfDocuments and key data per object. For gmw, cpt, bhrp, bhrg, bhrgt, sfr, guf, epc, epl (not for gld, gar, gmn, frd, gpd).
- `GET /{domein}/{type}/{versie}/objects/{broId}`: One full registration object, as XML in the BRO model (fullHistory=ja for wells and licences).
- `GET /{domein}/{type}/{versie}/bro-ids?bronhouder={kvk}`: All bro-ids of one source holder (Chamber of Commerce number required); registered=ja|nee.
- `GET /gm/v1/gmw-relations/{broId}`: For a well: the tubes and references to groundwater level (GLD), composition (GAR) and network (GMN) dossiers, as JSON.
- `GET /gm/v1/gmn-relations/{broId}`: For a network: its wells and tubes, as JSON.
- `GET /gm/gld/v1/seriesAsCsv/{broId}`: Groundwater level series as CSV: timestamp plus a value and remark column per kind (provisional, assessed, control, unknown).
- `GET /gm/gld/v1/objects/{broId}/observationsSummary`: Overview of the observations in a dossier (id, start, end, type, status), as JSON.
- `GET /gm/gld/v1/objects/{broId}/observations/{observationId}`: One observation as XML, with startTVPTime/endTVPTime or mostRecentTVP.
- `GET /gm/gld/v1/objectsAsCsv/{broId}`: The whole groundwater level dossier as CSV (rapportagetype, observatietype).
- `GET /gm/gmw/v1/groundlevelposition-as-csv/{broId}`: Ground level of a well over time, as CSV.
- `GET /gm/gmn/v1/networks`: List of monitoring networks, filterable by source holder, monitoring purpose and measuring points (without a filter: 204 empty).
- `GET /gu/gpd/v1/seriesAsCsv/{broId}`: Extracted and injected volumes of a groundwater production dossier as CSV.
- `POST /gm/gar/v1/table/dispatch`: Groundwater composition as a table for a dispatch result.

### Parameters
- `area.enclosingCircle`: Circle: center {lat, lon} (WGS84) and radius in kilometres. (tested)
- `area.boundingBox`: Box: lowerCorner and upperCorner, each {lat, lon}. (tested)
- `registrationPeriod`: {beginDate, endDate} as YYYY-MM-DD: when the object was registered. (tested)
- `correctionPeriod, constructionPeriod, removalPeriod`: Other periods in the search (for wells: construction and removal). (from the description)
- `qualityRegime, underReview, owner, deliveryAccountableParty`: Filters in the search: IMBRO or IMBRO/A, JA/NEE, Chamber of Commerce number of owner or source holder. (from the description)
- `bronhouder`: Chamber of Commerce number, required for bro-ids. (from the description)
- `fullHistory`: On objects: include earlier versions of the object. (from the description)
- `observationPeriodBeginDate, observationPeriodEndDate, filtered`: On GLD objects: only observations in a period. (from the description)
- `asISO8601`: On seriesAsCsv: timestamps as ISO 8601 (1956-09-28T12:00:00+01:00) instead of epoch milliseconds. (tested)
- `filterOnStatusQualityControl`: On seriesAsCsv: only values with this quality status. (from the description)
- `requestReference`: Own reference echoed in the response. (from the description)

### Fields in the responses (selection: well search result and series CSV) (9)
`brocom:broId` tekst, `brocom:qualityRegime` tekst, `brocom:objectRegistrationTime` datetime, `brocom:standardizedLocation` punt, `brocom:deliveredLocation` punt, `groundLevelPosition` getal, `wellConstructionDate` datum, `Beoordeelde Waarde [m]` getal, `Tijdstip` getal

### Limits and pitfalls
- A search that would return more than 2,000 objects is rejected: HTTP 400 with responseType 'rejection' and the reason 'Het aantal objecten is groter dan 2000' (more than 2000 objects). Make the area or period smaller. (tested)
- No key and no documented rate limit. A search returning 15 wells (31 kB XML) came back in 0.3 s. (tested)
- Errors are XML with responseType 'rejection' and a rejectionReason, also for an unknown bro-id ('Registratieobject GMW999999999999 bestaat niet.', HTTP 400). (tested)
- Dates differ per endpoint: observationsSummary gives 29-07-1971 (DD-MM-YYYY), seriesAsCsv epoch milliseconds (or ISO 8601 with asISO8601=true), XML ISO 8601. (tested)
- The OpenAPI descriptions are YAML (application/yaml), not JSON; tools that read only JSON must convert first. There are 15 services: /gm/{frd,gar,gld,gmn,gmw}/v1, /gm/v1, /sr/bhrp/v2, /sr/bhrg/v3, /sr/bhrgt/v2, /sr/cpt/v1, /sr/sfr/v2, /gu/{gpd,guf}/v1, /ep/{epc,epl}/v1. (tested)
- Wells with quality regime IMBRO/A (72,798 of 97,768) were migrated from older archives and are less complete. Removed wells are still included (deregistered/removal). (experience)
- robots.txt of publiek.broservices.nl returned HTTP 503 (16 Oct 2026): no rules to read. We only use the documented API with small calls. (tested)

### Example
```
https://publiek.broservices.nl/gm/v1/gmw-relations/GMW000000004076
```

## How to fetch it

### curl
```bash
curl https://publiek.broservices.nl/gm/v1/gmw-relations/GMW000000004076
```

### python
```python
import io, requests, pandas as pd

csv = requests.get("https://publiek.broservices.nl/gm/gld/v1/seriesAsCsv/GLD000000002796").text
df = pd.read_csv(io.StringIO(csv))
df["tijd"] = pd.to_datetime(df["Tijdstip"], unit="ms")
print(df[["tijd", "Beoordeelde Waarde [m]"]].tail())
```

## What you can build with it
- [Climate risk check for an address](https://www.opendataindex.nl/en/bouwen/klimaatrisico-adres.md): Type a postcode and house number and get one report on how the building fares on heat, flooding from rain, flooding from rivers and sea, drought and foundation problems, using the Klimaateffectatlas maps, the AHN elevation model, the BAG and real groundwater measurements from the BRO.


## Examples with real figures (1)

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

- **Groundwater monitoring wells in the BRO by year built: 2,786 wells in 2025, 2.2 times as much as in 1990** (2025). Table gm_gmw (grondwatermonitoringputten). [Example](https://www.opendataindex.nl/en/voorbeeld/bro-grondwaterputten-bouwjaar)
  - `https://api.pdok.nl/tno/bro-grondwatermonitoring-in-samenhang-karakteristieken/ogc/v1/collections/gm_gmw/items?f=json&limit=1000`
