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. Ongeveer 9,7 miljoen adressen,
elke werkdag bijgewerkt. 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 een bestaande postcode-API, zodat alleen de basis-URL wijzigt.
| 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/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. | `{"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.
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. Ongeveer 9,7 miljoen adressen,
elke werkdag bijgewerkt. 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 een bestaande postcode-API, zodat alleen de basis-URL wijzigt.
| 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/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. | `{"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.
## Specifiek voor dit Laravel-project
- Maak een service `App\Services\LocatieApi` en registreer hem in een
service provider. Injecteer hem, gebruik geen facade in domeincode.
- Gebruik `Illuminate\Support\Facades\Http` met `->timeout(5)->retry(2, 200)`.
- Zet de sleutel in `config/services.php` onder `locatieapi.key` en lees
hem uit `.env` als `LOCATIEAPI_KEY`.
- Cache met `Cache::remember("locatieapi:{postcode}:{number}", now()->addDay(), ...)`.
- Maak een Form Request-regel of een custom Rule `PostcodeExists` die het
adres controleert en bij een 404 een Nederlandse foutmelding geeft.
- Schrijf Pest-tests met `Http::fake()` voor 200, 400, 404 en 429.
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. Ongeveer 9,7 miljoen adressen,
elke werkdag bijgewerkt. 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 een bestaande postcode-API, zodat alleen de basis-URL wijzigt.
| 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/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. | `{"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.
## Specifiek voor dit Next.js-project
- Zet de lookup in een Route Handler onder `app/api/adres/route.ts`, zodat
de sleutel op de server blijft. Lees hem uit `process.env.LOCATIEAPI_KEY`.
- Gebruik `fetch` met `next: { revalidate: 86400 }` zodat Next het
antwoord een dag cachet.
- Geef vanuit de route handler alleen de velden door die de client nodig
heeft, en vertaal de statuscodes naar een eigen, kleine foutvorm.
- Bouw in de client een component met de velden postcode, huisnummer en
toevoeging die bij `onBlur` de route handler aanroept.
- Wil je autocomplete rechtstreeks vanuit de browser doen, gebruik dan een
aparte sleutel met alleen leesrechten en zet de toegestane origin erop.
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. Ongeveer 9,7 miljoen adressen,
elke werkdag bijgewerkt. 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 een bestaande postcode-API, zodat alleen de basis-URL wijzigt.
| 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/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. | `{"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.
## Specifiek voor dit WordPress- of WooCommerce-project
- Maak een kleine plugin in `wp-content/plugins/locatieapi/`.
- Zet de sleutel in `wp-config.php` als constante `LOCATIEAPI_KEY`. Nooit
in een optie die in de broncode van de pagina belandt.
- Registreer een AJAX-actie via `wp_ajax_` en `wp_ajax_nopriv_` en
controleer een nonce met `check_ajax_referer`.
- Roep de API server-side aan met `wp_remote_get` en een timeout van 5
seconden.
- Cache met `set_transient` en `DAY_IN_SECONDS`.
- Vul in de WooCommerce-checkout `billing_address_2` en `billing_city` aan
zodra `billing_postcode` en `billing_address_1` ingevuld zijn, en laat de
velden bewerkbaar zodat een afwijkend adres altijd mogelijk blijft.
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. Ongeveer 9,7 miljoen adressen,
elke werkdag bijgewerkt. 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 een bestaande postcode-API, zodat alleen de basis-URL wijzigt.
| 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/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. | `{"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.
## Specifiek voor dit Python-project
- Maak een module `locatieapi.py` met een `requests.Session` waarop de
header `X-Api-Key` al staat, of gebruik `httpx` als dat hier al gebruikt
wordt.
- Lees de sleutel uit `os.environ["LOCATIEAPI_KEY"]`.
- Cache met `functools.lru_cache` voor een kort draaiend script, en met
Redis of de cache van het framework voor een webapplicatie.
- Gooi eigen excepties: `AddressNotFound` bij 404, `InvalidAddress` bij 400
en `RateLimited` bij 429, zodat de aanroepende code er iets mee kan.
- Schrijf tests met `responses` of `respx` voor alle vier de statuscodes.