Postcode / Kadaster lookup
How to use this page:
- Date: Reference date. Choose a date in the past to retrieve the situtation at that time. Note: not all historic data is available.
- Province: Only select a province to retrieve an array-of-records for all places ("woonplaatsen") in that province. Also includes the area ("gemeente").
- Place: Select a place to retrieve a record with all postcodes in that place. The postcode is the key of the array, the value is the number of addresses in that specific postcode.
- Postcode: Retrieves an array-of-records with all address in that postcode. A record consists of:
number
: house numberchar
: letter suffix for the housenumber (e.g. "A").suffix
: othern housenumber suffix. Note: bothchar
andsuffix
can exists for a specific number.space
: name of the public space the property is connected to. Most often a street name.type
: space type. For example a road ("Weg"), terrain ("Terrein"), water ("Water"), or railway track ("Spoorbaan"). An emptytype
means the property is connected directly with the place.place
: "woonplaats".area
: municipality ("gemeente") the place belongs to.
- Number: If you specify both a postcode and a number the result will be filtered for this specific number.
You can also use this page as an API in both JSON and XML format.
Data is refreshed every 10th of the month.
Resources:
- https://www.kadaster.nl/-/bag-extract-koppelvlak
- https://service.pdok.nl/kadaster/adressen/atom/v1_0/downloads/lvbag-extract-nl.zip
- https://www.cbs.nl/nl-nl/onze-diensten/methoden/classificaties/overig/gemeentelijke-indelingen-per-jaar
Source to parse the data:
<?php
define('AREA_SOURCE','https://www.cbs.nl/-/media/cbs/onze-diensten/methoden/classificaties/overig/gemeenten-alfabetisch-*.xlsx'); //replace asterisk with next or this year
define('ZIP_SOURCE','https://service.pdok.nl/kadaster/adressen/atom/v1_0/downloads/lvbag-extract-nl.zip');
define('SUB_PREFIX','9999');
$area_filename = '/var/www/tmp/gemeenten-alfabetisch.xlsx';
$area_interval = 28 * 86400;
$zip_filename = '/var/www/tmp/lvbag-extract-nl.zip';
$zip_interval = 7 * 86400;
$temp_path = '/tmp/'; //incl trailing /
if(!is_file($area_filename) || (filemtime($area_filename) < time() - $area_interval)){
print("Downloading areas (gemeenten) file\n");
if(!@copy(str_replace('*',($year = date('Y')) + 1,AREA_SOURCE),$area_filename)) copy(str_replace('*',$year,AREA_SOURCE),$area_filename);
}
if(!is_file($zip_filename) || (filemtime($zip_filename) < time() - $zip_interval)){
print("Downloading data file Basisregistratie Adressen en Gebouwen (BAG) from Kadaster - this may take a while\n");
copy(ZIP_SOURCE,$zip_filename);
}
function xml_array($node){
$values = [];
foreach($node as $value) $values[] = strval($value);
return $values;
}
function parse_bag($sub,$tag,$active,$callback){
global $zip_filename,$temp_path;
print("Extracting '$sub' from data file\n");
$zip = new ZipArchive();
$zip->open($zip_filename);
for($i = 0; $i < $zip->numFiles; $i++) if(strpos($name = $zip->statIndex($i)['name'],$sub) === 0){
print("- Full name: $name\n");
$count = $bytes = 0;
$zip->extractTo($temp_path,$name);
$zip->close();
$zip->open($sub_zip_filename = $temp_path . $name);
for($i = 0; $i < $zip->numFiles; $i++){
$zip->extractTo($temp_path,$name = $zip->statIndex($i)['name']);
print("- Processing $name\r");
$f = fopen($data_filename = $temp_path . $name,'r');
$buffer = null;
while(!feof($f)){
$buffer .= fread($f,102400);
while($e = strpos($buffer,"</$tag>")){ //end tag found
$s = strpos($buffer,"<$tag>"); //start of start tag
$e += strlen($tag) + 3; //end of end tag
$xml = simplexml_load_string(preg_replace('/(<\\/?)[\\-\\w]+:/','$1',substr($buffer,$s,$e - $s))); //remove all namespaces
if($xml->status == $active) call_user_func($callback,$xml);
$buffer = substr($buffer,$e); //remove node from buffer
}
}
fclose($f);
$count++;
$bytes += filesize($data_filename);
unlink($data_filename);
}
$zip->close();
unlink($sub_zip_filename);
print("Total " . number_format($count) . " files, " . number_format($bytes / 1024 / 1024,2) . " Mb \n");
return true;
}
$zip->close();
die(">> sub-ZIP not found\n");
}
$provinces = $areas = $places = $spaces = $numbers = []; //provincie, gemeente, plaatsen, ruimten (m.n. wegen), huisnummers (incl. postcode)
print("Processing area file ...\n");
$zip = new ZipArchive();
$zip->open($area_filename); //XLSX file = ZIP file with XML files
$xml = simplexml_load_string($zip->getFromName('xl/sharedStrings.xml'));
$strings = [];
foreach($xml->si as $value) $strings[] = strval($value->t);
$xml = simplexml_load_string($zip->getFromName('xl/worksheets/sheet2.xml'));
$zip->close();
foreach($xml->sheetData->row as $row){
if(!array_key_exists($province = $strings[strval($row->c[3]->v)],$provinces)) $provinces[$province] = ['name' => $strings[strval($row->c[5]->v)]];
$areas[$strings[strval($row->c[0]->v)]] = [
'name' => $strings[strval($row->c[2]->v)],
'province' => $province
];
}
parse_bag(SUB_PREFIX . 'WPL','Objecten:Woonplaats','Woonplaats aangewezen',function($xml) use (&$places){
$places[strval($xml->identificatie)] = [
'name' => strval($xml->naam),
'areas' => []
];
});
parse_bag('GEM-WPL-RELATIE','gwr-product:GemeenteWoonplaatsRelatie','definitief',function($xml) use (&$places){
$places[strval($xml->gerelateerdeWoonplaats->identificatie)]['areas'][] = [
'area' => strval($xml->gerelateerdeGemeente->identificatie),
'start' => strval($xml->tijdvakgeldigheid->begindatumTijdvakGeldigheid),
'end' => strval($xml->tijdvakgeldigheid->einddatumTijdvakGeldigheid ?? null) ?: null,
];
});
parse_bag(SUB_PREFIX . 'OPR','Objecten:OpenbareRuimte','Naamgeving uitgegeven',function($xml) use (&$spaces){
$spaces[strval($xml->identificatie)] = [
'name' => strval($xml->naam),
'short' => strval($xml->verkorteNaam->VerkorteNaamOpenbareRuimte->verkorteNaam ?? null) ?: null,
'type' => strval($xml->type), //Weg,Water,Landschappelijk gebied,Terrein,Kunstwerk,Administratief gebied,Spoorbaan
'places' => xml_array($xml->ligtIn->WoonplaatsRef)
];
});
parse_bag(SUB_PREFIX . 'NUM','Objecten:Nummeraanduiding','Naamgeving uitgegeven',function($xml) use (&$numbers){
$numbers[$id = strval($xml->identificatie)] = [
'number' => strval($xml->huisnummer),
'char' => strval($xml->huisletter ?? null) ?: null,
'suffix' => strval($xml->huisnummertoevoeging ?? null) ?: null,
'postcodes' => xml_array($xml->postcode ?? []),
'start' => strval($xml->voorkomen->Voorkomen->beginGeldigheid),
'end' => strval($xml->voorkomen->Voorkomen->eindGeldigheid ?? null) ?: null,
'spaces' => xml_array($xml->ligtAan->OpenbareRuimteRef ?? []),
'places' => xml_array($xml->ligtIn->WoonplaatsRef ?? [])
];
});
print("\nTotal " . number_format(count($numbers)) . " numbers\n");