Aan de slag

Documentatie

Alles wat je nodig hebt om LocatieAPI in je applicatie te gebruiken. Van je eerste aanroep tot de sandbox, de foutcodes en de valkuilen.

Voor je codeerassistent

Laat je AI het werk doen

Eén knop zet een volledige instructie op je klembord: de basis-URL's, de header, alle endpoints met parameters en voorbeeldresponses, de foutcodes en de valkuilen, plus de opdracht om er in jouw project een adresopzoeker mee te bouwen. Plak hem in Claude Code, Cursor, Copilot of ChatGPT.

2.524 tokens, ongeveer

Variant: Algemeen

Bekijk de instructie voor Algemeen
Je gaat LocatieAPI in mijn project inbouwen. LocatieAPI is een Nederlandse
adres- en postcode-API: je geeft een postcode en huisnummer en krijgt straat,
plaats, gemeente, provincie en coördinaten terug. De data komt uit de BAG van
het Kadaster. Hieronder staat de volledige specificatie. Lees hem helemaal
voordat je code schrijft.

## 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 |

## Wat ik van je wil

1. Bouw een adresopzoeker in dit project met bovenstaande API.
2. Gebruik de HTTP-client die hier al gebruikt wordt. Voeg geen nieuwe
   afhankelijkheid toe als er al een client aanwezig is.
3. Zet de sleutel in de omgevingsvariabelen, nooit in de broncode en nooit in
   iets dat naar de browser gaat, tenzij het een sleutel met alleen
   leesrechten is waarop de toegestane origin is ingesteld.
4. Cache elk gevonden adres minstens 24 uur op de sleutel postcode plus
   huisnummer. Adressen veranderen zelden en het scheelt calls uit de bundel.
5. Behandel de statuscodes apart: 400 is een invoerfout die je aan de
   gebruiker toont, 404 betekent onbekend adres, 401 is een sleutelprobleem en
   429 betekent wachten en opnieuw proberen met exponentiële terugval.
6. Val netjes terug op handmatige invoer als de API niet bereikbaar is of
   traag reageert: zet een timeout van 5 seconden, laat de velden voor straat
   en plaats dan gewoon invulbaar en blokkeer het formulier niet.
7. Normaliseer de invoer voordat je de URL bouwt: haal spaties uit de
   postcode, maak hem hoofdletters, en stuur alleen het cijferdeel van het
   huisnummer mee.
8. Schrijf een test die de gelukkige route, de 404 en de 400 afdekt. Gebruik
   daarvoor de sandbox op https://sandbox.locatieapi.nl met de vaste testgevallen.

## Omgeving

Gebruik https://sandbox.locatieapi.nl zolang je aan het bouwen bent en zet hem om naar https://api.locatieapi.nl
als het werkt. De header blijft in beide gevallen X-Api-Key.

Aan de slag

  1. 1

    Vraag een gratis API-sleutel aan

    Een account is gratis en levert meteen een live- en een sandboxsleutel op. Je hoeft geen betaalgegevens achter te laten. Het gratis plan geeft 500 echte calls per maand en stopt daar hard, dus je krijgt nooit een onverwachte rekening.

    Sleutel aanvragen
  2. 2

    Test tegen de sandbox

    De sandbox draait op https://sandbox.locatieapi.nl en kent een vaste set adressen en foutgevallen. Sandboxverkeer is gratis, onbeperkt en telt nooit mee voor je bundel. Zo bouw je je koppeling af voordat je een call uitgeeft.

    Test de API in je browser
  3. 3

    Ga live

    Werkt het? Vervang sandbox.locatieapi.nl door api.locatieapi.nl en zet je livesleutel in de header. Verder verandert er niets aan je code.

Implementatie

Je stuurt een GET-verzoek met je sleutel in de header X-Api-Key en krijgt direct straat, plaats, gemeente en provincie terug, plus de coördinaten. Er is geen OAuth-dans, geen token dat verloopt en geen sleutel in de querystring.

URL-opbouw
https://api.locatieapi.nl/v3/lookup/{postcode}/{number}

De regels

  • Haal de spatie uit de postcode. 1021JT werkt, 1021 JT niet. Hoofdletters en kleine letters mogen allebei.
  • Stuur het huisnummer zonder toevoeging. Alleen het cijferdeel. 29a geeft een 400, geen 404.
  • Heb je de toevoeging wel nodig, gebruik dan /v1/addresses met letter en addition.
  • location is GeoJSON en zet de lengtegraad eerst: [longitude, latitude].
  • Cache het antwoord. Adressen wijzigen zelden en wij geven bij een 200 zelf al Cache-Control: public, max-age=86400 mee.

Voorbeeld request

Werkende code in negen talen, met de publieke sandboxsleutel er al in. Plakken en draaien.

curl -sS -i \
  -H "X-Api-Key: lat_test_demo_publiek_locatieapi_sandbox" \
  -H "Accept: application/json" \
  "https://api.locatieapi.nl/v3/lookup/1021JT/19"

Voorbeeld response

Alle velden zijn altijd aanwezig, ook als ze leeg zijn. Bij een postbus is location gelijk aan null en is street letterlijk Postbus.

200 application/json
{
  "postcode": "1021JT",
  "number": 19,
  "street": "Hamerstraat",
  "city": "Amsterdam",
  "municipality": "Amsterdam",
  "province": "Noord-Holland",
  "location": {
    "type": "Point",
    "coordinates": [4.92204151, 52.38488658]
  }
}

Veelgemaakte fouten

De API rechtstreeks vanuit de browser aanroepen

Bij veel adres-API's kan dit niet: de browser blokkeert het op CORS en je sleutel staat in de broncode van je pagina. Bij ons kan het wel, mits je het goed inricht. Zet op je sleutel de toegestane origins, bijvoorbeeld https://jouwwinkel.nl, en gebruik daarvoor een aparte sleutel met alleen leesrechten. Dan stuurt de API de juiste CORS-headers terug en kan autocomplete rechtstreeks vanuit je checkout draaien, zonder tussenlaag.

Een sleutel zonder ingestelde origin werkt bewust alleen server-side. Zo kan een sleutel die per ongeluk in een frontend-bundel belandt niet zomaar door een ander domein gebruikt worden. Zet nooit een livesleutel met schrijfrechten of een ruime bundel in de browser.

Een request payload of Content-Type meesturen bij een GET

/v3/lookup en de andere GET-endpoints halen alles uit de URL. Stuur geen body mee en zet geen Content-Type. Sommige HTTP-clients doen dat automatisch zodra je een array meegeeft; controleer dat als je een onverwachte 400 krijgt.

Alleen /v1/bulk/lookup en /v1/validate zijn POST-endpoints. Die verwachten juist wel Content-Type: application/json.

De coördinaten omdraaien

location.coordinates is GeoJSON: [longitude, latitude]. Leaflet, Google Maps en de meeste andere kaartbibliotheken willen [latitude, longitude]. Draai ze dus om voordat je een marker plaatst, anders staat je adres in de Noordzee bij Somalië.

Sandbox

De sandbox is gratis en onbeperkt, in elk plan, ook het gratis plan. Sandboxverkeer telt nooit mee voor je bundel. Hij draait op een vaste set adressen en foutgevallen, zodat je je koppeling en je tests kunt bouwen op antwoorden die niet veranderen.

Basis-URL sandbox
https://sandbox.locatieapi.nl/v3/lookup/{postcode}/{number}

Dit zijn alle testcombinaties:

Vaste testcombinaties in de sandbox
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

Let op bij 6545CA/29

De sandbox geeft daar Waldeck Pyrmontsingel terug, terwijl de BAG voor dat adres Binderskampweg zegt. Dat is bewust: de sandbox is een vaste set die precies gelijk is aan die van postcodeapi.nu, zodat je bestaande tests blijven slagen. De productie-API volgt de BAG.