# LocatieAPI, volledige documentatie Nederlandse adres- en postcode-API van Score Media B.V. Deze tekst bevat de hele publieke documentatie, zodat een taalmodel hem in één keer kan inlezen. Bijgewerkt uit dezelfde bron als de documentatiepagina's op https://locatieapi.nl. ## Basis - Live: `https://api.locatieapi.nl` - Sandbox: `https://sandbox.locatieapi.nl`, gratis, vaste testset, telt niet mee voor de bundel - Altijd werkende terugval op hetzelfde pad: `https://locatieapi.nl/api` - Authenticatie: stuur de header `X-Api-Key: lat_test_demo_publiek_locatieapi_sandbox`. Er is geen andere methode, geen OAuth en geen sleutel in de querystring. - Alle responses zijn JSON. Foutresponses van v3 hebben `Content-Type: application/problem+json`. ## Endpoints ### GET /v3/lookup/{postcode}/{number} Eén adres op postcode en huisnummer. Response-compatibel met postcodeapi.nu. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `postcode` | string | ja | P6 zonder spatie, hoofdletters of kleine letters. Patroon `^[0-9]{4}[a-zA-Z]{2}$`. | | `number` | integer | ja | Huisnummer als geheel getal. Een toevoeging in het pad geeft `400`, geen `404`. | Voorbeeldresponse: ```json { "postcode": "1021JT", "number": 19, "street": "Hamerstraat", "city": "Amsterdam", "municipality": "Amsterdam", "province": "Noord-Holland", "location": { "type": "Point", "coordinates": [4.92204151, 52.38488658] } } ``` ### GET /v1/addresses Alle treffers op een postcode en huisnummer, inclusief letters en toevoegingen. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `postcode` | string | ja | P6 zonder spatie. | | `number` | integer | nee | Huisnummer. Zonder nummer krijg je de hele postcode terug. | | `letter` | string | nee | Huisletter, één teken. | | `addition` | string | nee | Huisnummertoevoeging. | Voorbeeldresponse: ```json { "data": [ { "id": "0363200012101386", "postcode": "1021JT", "number": 19, "letter": null, "addition": null, "street": "Hamerstraat", "city": "Amsterdam", "municipality": "Amsterdam", "province": "Noord-Holland", "location": { "type": "Point", "coordinates": [4.92204151, 52.38488658] }, "purposes": ["woonfunctie"], "surface": 98, "constructionYear": 1930, "type": "verblijfsobject" } ] } ``` ### GET /v1/addresses/{bagId} Eén adres op het BAG-nummeraanduidingsnummer. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `bagId` | string | ja | BAG-nummeraanduiding, zestien cijfers. | ### GET /v1/postcodes/{postcode} Samenvatting per P6: straat, plaats, gemeente, provincie, zwaartepunt en aantal adressen. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `postcode` | string | ja | P6 zonder spatie. | Voorbeeldresponse: ```json { "postcode": "1021JT", "street": "Hamerstraat", "city": "Amsterdam", "municipality": "Amsterdam", "province": "Noord-Holland", "location": { "type": "Point", "coordinates": [4.92204151, 52.38488658] }, "addressCount": 64, "numberMin": 1, "numberMax": 121 } ``` ### GET /v1/autocomplete Type-ahead op adres, straat of plaats. Bedoeld voor een checkout of zoekveld. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `q` | string | ja | Zoekterm, minimaal twee tekens. | | `type` | string | nee | `address`, `street` of `city`. Standaard `address`. | | `limit` | integer | nee | Aantal suggesties, 1 tot 25. Standaard 10. | Voorbeeldresponse: ```json { "data": [ { "label": "Hamerstraat 19, 1021 JT Amsterdam", "type": "address", "postcode": "1021JT", "number": 19, "street": "Hamerstraat", "city": "Amsterdam" } ] } ``` ### POST /v1/bulk/lookup Maximaal 1.000 combinaties in één verzoek. Elke regel telt als één call. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `addresses` | array | ja | Lijst van objecten met `postcode` en `number`. | Voorbeeldresponse: ```json { "data": [ { "postcode": "1021JT", "number": 19, "status": 200, "address": { "street": "Hamerstraat", "city": "Amsterdam", "municipality": "Amsterdam", "province": "Noord-Holland", "location": { "type": "Point", "coordinates": [4.92204151, 52.38488658] } } }, { "postcode": "6545CA", "number": 299, "status": 404, "address": null } ] } ``` ### POST /v1/validate Controleert een ingevoerd adres en geeft een correctievoorstel terug. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `postcode` | string | ja | Postcode zoals ingevoerd, met of zonder spatie. | | `number` | string | ja | Huisnummer zoals ingevoerd, toevoeging mag erin staan. | | `street` | string | nee | Straatnaam zoals ingevoerd, voor de vergelijking. | | `city` | string | nee | Plaats zoals ingevoerd, voor de vergelijking. | Voorbeeldresponse: ```json { "valid": false, "reason": "street_mismatch", "suggestion": { "postcode": "1021JT", "number": 19, "letter": null, "addition": null, "street": "Hamerstraat", "city": "Amsterdam" } } ``` ### GET /v1/reverse De dichtstbijzijnde adressen bij een coördinaat, met afstand in meters. | Parameter | Type | Verplicht | Omschrijving | |---|---|---|---| | `lat` | number | ja | Breedtegraad in WGS84. | | `lon` | number | ja | Lengtegraad in WGS84. | | `radius` | integer | nee | Zoekstraal in meters, 1 tot 1.000. Standaard 100. | Voorbeeldresponse: ```json { "data": [ { "distance": 12, "postcode": "1021JT", "number": 19, "street": "Hamerstraat", "city": "Amsterdam", "location": { "type": "Point", "coordinates": [4.92204151, 52.38488658] } } ] } ``` ## Foutcodes | Status | Titel | Wanneer | Body | |---|---|---|---| | 400 | Request validation failed | De postcode voldoet niet aan het patroon, of het huisnummer is geen geheel getal. | `{"title":"Request validation failed","invalidParams":[{"name":"number","reason":"should be integer"}]}` | | 401 | Invalid API key | De header X-Api-Key ontbreekt, is onbekend of is ingetrokken. | `{"title":"Invalid API key"}` | | 404 | Resource not found | De combinatie bestaat niet in de BAG. | `{"title":"Resource not found"}` | | 429 | Rate limit exceeded | Je zit boven je calls per seconde of boven je maandbundel. | `{"title":"Rate limit exceeded"}` | ## Responseheaders op elke call - `X-RateLimit-Limit`: Het aantal calls per seconde dat bij je plan hoort. - `X-RateLimit-Remaining`: Wat er in het huidige venster van dat aantal over is. - `X-RateLimit-Reset`: Aantal seconden tot het venster opnieuw begint. - `X-Quota-Limit`: De maandbundel van je plan in aantal calls. - `X-Quota-Remaining`: Wat er van die bundel over is in de lopende factuurperiode. - `X-Quota-Reset`: Unix-tijdstip waarop de volgende factuurperiode begint. - `X-Request-Id`: Uniek nummer per verzoek. Noem dit bij een supportvraag. - `Cache-Control`: Bij een 200: `public, max-age=86400`. Adressen veranderen zelden. ## Valkuilen - **Postcode zonder spatie** De API accepteert `1021JT`, niet `1021 JT`. Haal de spatie er in je eigen code uit voordat je de URL bouwt. - **Huisnummer als geheel getal** In `/v3/lookup` hoort alleen het cijferdeel. `29a` geeft een `400` en geen `404`. - **Toevoegingen horen bij /v1/addresses** Wil je huisletters en toevoegingen, gebruik dan `/v1/addresses` met `letter` en `addition`. - **location is [longitude, latitude]** GeoJSON zet de lengtegraad eerst. Dat is de omgekeerde volgorde van wat de meeste kaartbibliotheken als "lat, lng" tonen. - **location kan null zijn** Bij een postbus is er geen coördinaat en is `street` letterlijk `Postbus`. - **Fouten zijn problem+json** Foutresponses van v3 hebben `Content-Type: application/problem+json` en een veld `title`, geen `message`. ## Sandbox De sandbox draait op `https://sandbox.locatieapi.nl` en kent precies deze gevallen: | Postcode | Huisnummer | Status | Resultaat | |---|---|---|---| | 6545CA | 29 | 200 | Waldeck Pyrmontsingel, Nijmegen, Gelderland | | 1021JT | 19 | 200 | Hamerstraat, Amsterdam, Noord-Holland | | 5038EA | 17 | 200 | Stationsstraat, Tilburg, Noord-Brabant | | 3030AC | 100 | 200 | Postbus, Leusden, Utrecht. location is null | | 6545CA | 29a | 400 | number should be integer | | 6545C | 29 | 400 | postcode voldoet niet aan het patroon | | 6545CA | 299 | 404 | Resource not found | ## Voorbeeldverzoek in negen talen De voorbeelden hieronder draaien tegen de sandbox met de publieke sandboxsleutel en werken zonder account. ### cURL ``` curl -sS -i \ -H "X-Api-Key: lat_test_demo_publiek_locatieapi_sandbox" \ -H "Accept: application/json" \ "https://sandbox.locatieapi.nl/v3/lookup/1021JT/19" ``` ### PHP ``` true, CURLOPT_TIMEOUT => 5, CURLOPT_HTTPHEADER => [ 'X-Api-Key: lat_test_demo_publiek_locatieapi_sandbox', 'Accept: application/json', ], ]); $body = curl_exec($curl); $status = curl_getinfo($curl, CURLINFO_RESPONSE_CODE); curl_close($curl); if ($status !== 200) { throw new RuntimeException("LocatieAPI gaf status {$status}: {$body}"); } $address = json_decode($body, true); echo $address['street'].' '.$address['number'].', '.$address['city'].PHP_EOL; ``` ### Laravel ``` addDay(), function () use ($postcode, $number) { $response = Http::withHeaders(['X-Api-Key' => config('services.locatieapi.key')]) ->acceptJson() ->timeout(5) ->retry(2, 200) ->get("https://sandbox.locatieapi.nl/v3/lookup/{$postcode}/{$number}"); if ($response->status() === 404) { return null; } return $response->throw()->json(); }); } ``` ### Node.js ``` // Node 18 of nieuwer: fetch zit in de standaardbibliotheek. const postcode = '1021JT'; const number = 19; const response = await fetch(`https://sandbox.locatieapi.nl/v3/lookup/${postcode}/${number}`, { headers: { 'X-Api-Key': 'lat_test_demo_publiek_locatieapi_sandbox', Accept: 'application/json', }, }); if (!response.ok) { const problem = await response.json(); throw new Error(`LocatieAPI ${response.status}: ${problem.title}`); } const address = await response.json(); console.log(`${address.street} ${address.number}, ${address.city}`); ``` ### Python ``` import requests postcode, number = "1021JT", 19 response = requests.get( f"https://sandbox.locatieapi.nl/v3/lookup/{postcode}/{number}", headers={"X-Api-Key": "lat_test_demo_publiek_locatieapi_sandbox", "Accept": "application/json"}, timeout=5, ) if response.status_code == 404: raise LookupError("Adres niet gevonden") response.raise_for_status() address = response.json() print(f"{address['street']} {address['number']}, {address['city']}") ``` ### Go ``` package main import ( "encoding/json" "fmt" "log" "net/http" "time" ) type Address struct { Postcode string `json:"postcode"` Number int `json:"number"` Street string `json:"street"` City string `json:"city"` } func main() { req, err := http.NewRequest(http.MethodGet, "https://sandbox.locatieapi.nl/v3/lookup/1021JT/19", nil) if err != nil { log.Fatal(err) } req.Header.Set("X-Api-Key", "lat_test_demo_publiek_locatieapi_sandbox") req.Header.Set("Accept", "application/json") client := &http.Client{Timeout: 5 * time.Second} res, err := client.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() if res.StatusCode != http.StatusOK { log.Fatalf("LocatieAPI gaf status %d", res.StatusCode) } var address Address if err := json.NewDecoder(res.Body).Decode(&address); err != nil { log.Fatal(err) } fmt.Printf("%s %d, %s\n", address.Street, address.Number, address.City) } ``` ### Java ``` import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; public class LocatieApiVoorbeeld { public static void main(String[] args) throws Exception { HttpRequest request = HttpRequest.newBuilder() .uri(URI.create("https://sandbox.locatieapi.nl/v3/lookup/1021JT/19")) .header("X-Api-Key", "lat_test_demo_publiek_locatieapi_sandbox") .header("Accept", "application/json") .timeout(Duration.ofSeconds(5)) .GET() .build(); HttpClient client = HttpClient.newHttpClient(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() != 200) { throw new IllegalStateException("LocatieAPI gaf status " + response.statusCode()); } System.out.println(response.body()); } } ``` ### C# ``` using System; using System.Net.Http; using System.Net.Http.Json; using System.Threading.Tasks; public record Address(string Postcode, int Number, string Street, string City); public static class LocatieApi { public static async Task Main() { using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) }; client.DefaultRequestHeaders.Add("X-Api-Key", "lat_test_demo_publiek_locatieapi_sandbox"); var response = await client.GetAsync("https://sandbox.locatieapi.nl/v3/lookup/1021JT/19"); if (!response.IsSuccessStatusCode) { throw new InvalidOperationException($"LocatieAPI gaf status {(int)response.StatusCode}"); } var address = await response.Content.ReadFromJsonAsync
(); Console.WriteLine($"{address!.Street} {address.Number}, {address.City}"); } } ``` ### Ruby ``` require "json" require "net/http" uri = URI("https://sandbox.locatieapi.nl/v3/lookup/1021JT/19") request = Net::HTTP::Get.new(uri) request["X-Api-Key"] = "lat_test_demo_publiek_locatieapi_sandbox" request["Accept"] = "application/json" response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true, open_timeout: 5, read_timeout: 5) do |http| http.request(request) end raise "LocatieAPI gaf status #{response.code}" unless response.code == "200" address = JSON.parse(response.body) puts "#{address['street']} #{address['number']}, #{address['city']}" ``` ## Prijzen Alle bedragen zijn exclusief 21% btw. | Plan | Per maand | Per jaar | Calls per maand | Calls per seconde | Sleutels | |---|---|---|---|---|---| | Gratis | € 0,00 | - | 500 | 3 | 2 | | Starter | € 4,95 | € 53,40 | 1.000 | 5 | 2 | | Klein | € 13,95 | € 150,60 | 5.000 | 10 | 5 | | Standaard | € 34,95 | € 377,40 | 20.000 | 15 | 10 | | Plus | € 69,95 | € 755,40 | 50.000 | 25 | 25 | | Pro | € 119,95 | € 1.295,40 | 100.000 | 50 | onbeperkt | | Enterprise | op aanvraag | - | 250.000+ | 100 | onbeperkt | - Sandbox-verkeer telt nooit mee voor de bundel. - Betaalde plannen krijgen 10% coulance boven de bundel; daarboven volgt een 429. - Het gratis plan stopt hard op de bundel, zonder naheffing. - Verbruik loopt mee met je factuurperiode, niet met de kalendermaand. - Je krijgt een waarschuwingsmail bij 80% en bij 100% van de bundel. - Het jaarabonnement is 10% goedkoper dan twaalf maanden los. ## Overstappen van postcodeapi.nu De v3-endpoints zijn response-compatibel. In de praktijk wijzigt alleen de basis-URL: vervang `https://api.postcodeapi.nu/v3` door `https://api.locatieapi.nl/v3`. De header heet bij ons ook `X-Api-Key`. Veld voor veld is de response gelijk, inclusief de GeoJSON-volgorde [longitude, latitude] en de problem+json-fouten. ## Meer - Statuspagina: https://locatieapi.nl/status - Privacyverklaring: https://locatieapi.nl/privacy - Verwerkersovereenkomst: https://locatieapi.nl/verwerkersovereenkomst - Contact: https://locatieapi.nl/contact