Belgische bedrijfs-API: codevoorbeelden in curl, JavaScript en Python
Werkende codevoorbeelden voor de Belgische bedrijfsgegevens-API: lookup, zoeken op naam en postcode, btw-controle, foutafhandeling en rate limits.
Een API leer je het snelst kennen met code die meteen werkt. Deze gids geeft voorbeelden in curl, JavaScript (fetch, Node 18 of nieuwer) en Python (requests) voor de vier dingen die je het vaakst nodig hebt: een bedrijf opzoeken met het ondernemingsnummer, zoeken op naam en postcode, een btw-nummer controleren en netjes omgaan met fouten en rate limits. De algemene opzet van een integratie staat in de gids over het integreren van de API, hier ligt de nadruk op copy-paste.
Voorbereiding
De basis-URL is https://api.kvkbase.nl. Elke aanvraag draagt de header Authorization: Bearer YOUR_API_KEY. Je sleutel maak je aan na je registratie, het gratis plan bevat 50 tokens per maand en vraagt geen kredietkaart. Prijzen en grotere plannen vind je op de prijspagina.
In alle voorbeelden hieronder lezen we de sleutel uit een omgevingsvariabele KVKBASE_API_KEY. Het ondernemingsnummer 0123.456.789 is een illustratief voorbeeld, vervang het door een nummer dat je kent. Elke endpoint is beschreven in de API-referentie.
Een bedrijf opzoeken met het ondernemingsnummer
Het nummer mag met of zonder punten en met of zonder BE-prefix. De optionele parameter lang (nl, fr, de of en) bepaalt de taal van labels zoals de rechtsvorm.
curl -s "https://api.kvkbase.nl/v1/be/lookup/0123.456.789?lang=nl" \
-H "Authorization: Bearer YOUR_API_KEY"
In JavaScript schrijf je een kleine functie die bij een fout een duidelijke uitzondering gooit. Zo kan de code die haar aanroept de statuscode en de foutcode lezen.
const BASE = "https://api.kvkbase.nl";
const KEY = process.env.KVKBASE_API_KEY;
async function api(path, init = {}) {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: { Authorization: `Bearer ${KEY}`, ...init.headers },
signal: AbortSignal.timeout(10_000),
});
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw Object.assign(new Error(body?.error?.message ?? `HTTP ${res.status}`), {
status: res.status,
code: body?.error?.code,
retryAfter: Number(res.headers.get("Retry-After")) || null,
});
}
return res.json();
}
const company = await api("/v1/be/lookup/0123.456.789?lang=nl");
console.log(company.name, company.legalFormShort, company.isActive);
Dezelfde aanroep in Python met requests:
import os
import requests
BASE = "https://api.kvkbase.nl"
SESSION = requests.Session()
SESSION.headers["Authorization"] = f"Bearer {os.environ['KVKBASE_API_KEY']}"
def api(path, **kwargs):
r = SESSION.get(f"{BASE}{path}", timeout=10, **kwargs)
r.raise_for_status()
return r.json()
company = api("/v1/be/lookup/0123.456.789", params={"lang": "nl"})
print(company["name"], company["legalFormShort"], company["isActive"])
Het antwoord bevat onder meer cbeNumber, cbeNumberFormatted, name, tradingNames, legalForm, legalFormShort, entityType, address, isActive, juridicalSituation, activities, establishments en vat. Het blok source toont met cachedAt en stale hoe vers het antwoord is.
Zoeken op naam en postcode
Heb je geen nummer, dan gebruik je GET /v1/be/search. Je geeft een naam in q (minstens twee tekens), een postalCode van vier cijfers, of allebei. Allebei geeft de scherpste resultaten.
curl -s "https://api.kvkbase.nl/v1/be/search?q=bakkerij&postalCode=9000&pageSize=10" \
-H "Authorization: Bearer YOUR_API_KEY"
const params = new URLSearchParams({ q: "bakkerij", postalCode: "9000", pageSize: "10" });
const { results, total, capped } = await api(`/v1/be/search?${params}`);
for (const hit of results) {
console.log(hit.cbeNumberFormatted, hit.name, hit.postalCode, hit.city);
}
if (capped) console.log("Mogelijk meer treffers: verfijn de zoekopdracht.");
data = api("/v1/be/search", params={"q": "bakkerij", "postalCode": "9000", "pageSize": 10})
for hit in data["results"]:
print(hit["cbeNumberFormatted"], hit["name"], hit["postalCode"], hit["city"])
if data["capped"]:
print("Mogelijk meer treffers: verfijn de zoekopdracht.")
Een zoekopdracht geeft nooit meer dan 25 treffers terug, ook niet als je page ophoogt. De parameters page en pageSize knippen alleen in diezelfde 25. Waar capped is true, bestaan er mogelijk meer bedrijven en moet je verfijnen. Meer over dat patroon staat in de gids over zoeken op postcode en sector.
Een btw-nummer controleren
Voor de btw-controle roep je GET /v1/be/validate/vat/{btw-nummer} aan. Een Belgisch btw-nummer is BE gevolgd door het ondernemingsnummer, zonder punten.
curl -s "https://api.kvkbase.nl/v1/be/validate/vat/BE0123456789" \
-H "Authorization: Bearer YOUR_API_KEY"
const vat = await api("/v1/be/validate/vat/BE0123456789");
if (vat.status === "validated") {
console.log(vat.valid ? "Btw-nummer actief" : "Btw-nummer niet geldig", vat.name);
} else if (vat.status === "stale") {
console.log("Oud VIES-antwoord van", vat.validatedAt, "(VIES was niet bereikbaar)");
} else {
console.log("Foutief formaat of controlegetal");
}
Het antwoord bevat vatNumber, valid, name, address, validatedAt, status en checksumValid. De waarde validated betekent dat VIES het antwoord gaf. Is VIES even onbereikbaar, dan kan status stale zijn, een eerder opgeslagen antwoord met datum, en bij een nummer dat het controlegetal niet haalt is het malformed. Behandel stale nooit als een verse bevestiging. Hoe het controlegetal werkt lees je in de gids over de opbouw van het btw-nummer.
Foutafhandeling
Elke fout heeft dezelfde vorm: {"error": {"code": "...", "message": "..."}}. Beslis op basis van de statuscode en de code.
| Code | Status | Wat doen |
|---|---|---|
INVALID_CBE_NUMBER | 400 | Niet herhalen, laat de gebruiker het nummer corrigeren |
INVALID_QUERY | 400 | Zoekopdracht aanpassen (naam te kort, postcode geen vier cijfers) |
NOT_FOUND | 404 | Niet herhalen, het nummer bestaat niet in de gegevens |
RATE_LIMIT_EXCEEDED / QUOTA_EXCEEDED | 429 | Zie de volgende sectie |
UPSTREAM_ERROR | 502 | Beperkt herhalen met backoff |
UPSTREAM_BUDGET | 503 | Wachten tot Retry-After en later opnieuw |
Belangrijk onderscheid: een 404 betekent dat het nummer niet bestaat, een 502 of 503 betekent dat we het even niet konden nagaan. Verwar die twee niet, anders markeer je een bestaand bedrijf als onbekend.
Rate limits en herhaalpogingen
Een 429 kan twee oorzaken hebben. Bij een rate limit zit je te snel: het antwoord bevat Retry-After in seconden en de headers X-RateLimit-Limit en X-RateLimit-Remaining tonen je marge. Bij een opgebruikt maandquotum heeft wachten geen zin, dan schaal je op of wacht je tot de volgende maand.
async function apiWithRetry(path, attempts = 4) {
for (let i = 0; i < attempts; i++) {
try {
return await api(path);
} catch (e) {
const retryable = [429, 502, 503].includes(e.status) && e.code !== "QUOTA_EXCEEDED";
if (!retryable || i === attempts - 1) throw e;
const wait = e.retryAfter ?? 2 ** i;
await new Promise((r) => setTimeout(r, wait * 1000));
}
}
}
import time
def api_with_retry(path, attempts=4, **kwargs):
for i in range(attempts):
try:
return api(path, **kwargs)
except requests.HTTPError as e:
r = e.response
code = (r.json().get("error") or {}).get("code") if r.content else None
retryable = r.status_code in (429, 502, 503) and code != "QUOTA_EXCEEDED"
if not retryable or i == attempts - 1:
raise
time.sleep(int(r.headers.get("Retry-After", 2 ** i)))
Houd een eigen teller bij als je veel oproepen doet, en lees X-RateLimit-Remaining om vooraf af te remmen in plaats van achteraf te herstellen.
Tips voor productie
- Zet altijd een time-out op je oproepen.
- Valideer ondernemingsnummers lokaal voor je belt, zie de gids over het controlegetal.
- Bewaar antwoorden met een eigen tijdstip, zodat je niet voor elke weergave opnieuw betaalt.
- Toon natuurlijke personen (eenmanszaken) niet zomaar publiek, hun adres kan privé zijn.
Verder lezen
Wil je een hele lijst nummers verwerken, lees dan de gids over bedrijven in bulk controleren. Een overzicht van het product staat op de Belgische landingspagina, en je kan de werking eerst proberen met de gratis tool om een ondernemingsnummer op te zoeken. Over het verschil tussen de nummers lees je meer in de gids ondernemingsnummer en btw-nummer.