Belgische bedrijfsgegevens API integreren: gids voor developers
Belgische bedrijfsgegevens API integreren in je CRM, facturatie of checkout: authenticatie, lookup, batch, foutafhandeling, caching en codevoorbeelden.
Wie Belgische bedrijfsgegevens in een eigen applicatie wil gebruiken, wil meestal dezelfde drie dingen: een nummer of naam omzetten naar een volledig bedrijfsprofiel, dat profiel in een formulier of CRM laten landen, en dit doen zonder bij elke storing of typefout in de problemen te komen. Deze gids loopt door de integratie met de KVKBase API, van de eerste curl tot een robuuste productieflow. Het gaat om officiële Belgische bedrijfsgegevens, geleverd als eenvoudige JSON.
Basis: authenticatie en endpoints
De basis-URL is https://api.kvkbase.nl. Elke aanvraag draagt een bearer-token in de header Authorization. Maak een sleutel aan via /signup, het gratis plan heeft 50 tokens per maand en vraagt geen kredietkaart. Zet de sleutel in een omgevingsvariabele, nooit in front-endcode.
| Endpoint | Doel | Kost |
|---|---|---|
GET /v1/be/lookup/{ondernemingsnummer} | Volledig profiel van één onderneming | 1 token |
GET /v1/be/search?q=...&postalCode=... | Zoeken op naam, max. 25 resultaten | 1 token |
POST /v1/be/lookup/batch | Tot 25 nummers per oproep | 1 token per nummer |
GET /v1/be/validate/vat/{btw-nummer} | Btw-controle via VIES | 1 token |
Daarnaast accepteert GET /v1/lookup/{number} ook Belgische nummers, handig als je al een algemene integratie hebt. De volledige veldbeschrijving staat in de API-referentie, prijsdetails op /pricing.
Eerste lookup
curl -s "https://api.kvkbase.nl/v1/be/lookup/0821.015.819?lang=nl" \
-H "Authorization: Bearer YOUR_API_KEY"
Het nummer mag met of zonder punten, met of zonder BE-prefix. De parameter lang (nl, fr, de of en) bepaalt de taal van labels zoals legalForm en juridicalSituation. Standaard is dat nl.
Normaliseren en valideren voor je belt
Gebruikers typen BE 0821 015 819, plakken spaties mee of vergeten de leidende nul. Ruim dat op in je eigen code, en controleer het controlegetal. Een ondernemingsnummer heeft tien cijfers, begint met 0 of 1, en de laatste twee cijfers zijn 97 min (de eerste acht cijfers modulo 97). Meer uitleg vind je in de gids over opzoeken.
export function normalizeCompanyNumber(input) {
let digits = String(input).replace(/\D/g, "");
if (digits.length === 9) digits = "0" + digits; // older 9-digit numbers
if (!/^[01]\d{9}$/.test(digits)) return null;
const base = Number(digits.slice(0, 8));
const check = Number(digits.slice(8));
return 97 - (base % 97) === check ? digits : null;
}
Geeft de functie null, toon dan meteen een foutmelding in het formulier. Je bespaart een netwerkoproep. Ook als je het toch doorstuurt, is er geen verlies: een misvormd nummer wordt aan onze kant geweigerd voor er opgezocht wordt en kost niets.
Lookup in JavaScript en Python
async function lookupCompany(number) {
const res = await fetch(
`https://api.kvkbase.nl/v1/be/lookup/${number}?lang=nl`,
{ headers: { Authorization: `Bearer ${process.env.KVKBASE_API_KEY}` } }
);
if (!res.ok) {
const body = await res.text().catch(() => "");
throw Object.assign(new Error(`HTTP ${res.status}: ${body}`), {
status: res.status,
});
}
return res.json();
}
import os
import requests
def lookup_company(number: str) -> dict:
r = requests.get(
f"https://api.kvkbase.nl/v1/be/lookup/{number}",
params={"lang": "nl"},
headers={"Authorization": f"Bearer {os.environ['KVKBASE_API_KEY']}"},
timeout=10,
)
r.raise_for_status()
return r.json()
Zet altijd een time-out. Een hangende oproep in een checkout is erger dan een foutmelding.
Zoeken en batch
Heb je enkel een naam, gebruik dan search. Een q van minstens twee tekens is verplicht, postalCode (vier cijfers) versmalt de resultaten sterk. Elke hit bevat onder meer cbeNumber, cbeNumberFormatted, name, entityType, legalForm, postcode en gemeente.
curl -s "https://api.kvkbase.nl/v1/be/search?q=bakkerij&postalCode=9000&pageSize=10" \
-H "Authorization: Bearer YOUR_API_KEY"
Voor lijsten gebruik je de batch-oproep. Maximaal 25 nummers per aanvraag, het antwoord is een object met je nummers als sleutel, en elk item is {success, data} of {success, error}. Een nummer dat niet gevonden wordt, krijgt dus een eigen foutregel. Een nummer met een ongeldig formaat of controlegetal wordt wel voor de hele batch geweigerd (HTTP 400), controleer ze dus vooraf.
const res = await fetch("https://api.kvkbase.nl/v1/be/lookup/batch", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.KVKBASE_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ cbeNumbers: numbers.slice(0, 25), lang: "nl" }),
});
const results = await res.json();
Knip grotere lijsten zelf in stukken van 25 en dedupliceer eerst: elk nummer in een batch kost een token.
Foutafhandeling en retries
| Code | Status | Wat doen |
|---|---|---|
INVALID_CBE_NUMBER | 400 | Niet herhalen, gebruiker laten corrigeren |
NOT_FOUND | 404 | Niet herhalen, nummer bestaat niet in de gegevens |
QUOTA_EXCEEDED / rate limit | 429 | Wachten en trager aanvragen, of plan opschalen |
UPSTREAM_ERROR | 502 | Beperkt herhalen met backoff |
UPSTREAM_BUDGET | 503 | Later opnieuw proberen |
Herhaal enkel bij 429, 502 en 503, en gebruik exponentiele backoff met een plafond van drie of vier pogingen. Een 4xx-fout herhalen heeft geen zin.
import time
RETRYABLE = {429, 502, 503}
def lookup_with_retry(number, attempts=4):
for i in range(attempts):
try:
return lookup_company(number)
except requests.HTTPError as e:
status = e.response.status_code
if status not in RETRYABLE or i == attempts - 1:
raise
time.sleep(2 ** i)
Opslaan en cachen
Bedrijfsgegevens veranderen traag. De meeste bedrijven verhuizen of wijzigen zelden van rechtsvorm. Bewaar daarom het volledige antwoord in je eigen database, met cbeNumber als sleutel en een eigen fetchedAt. Een paar richtlijnen:
- Toon in je eigen UI het opvraagmoment, bijvoorbeeld “gegevens van 2026-11-10”.
- Ververs actieve klanten op een vast ritme (maandelijks of per kwartaal) en altijd vlak voor een grote factuur of contract.
- Houd
isActiveenjuridicalSituationscherper in de gaten dan adresgegevens. - Het antwoord bevat
source.cachedAtensource.stale, zodat je weet hoe vers een kopie is. Wij bewaren kopieën, waardoor herhaalde lookups snel zijn, maar de gegevens zijn niet realtime en we garanderen geen volledigheid.
Een eigen cache bespaart tokens: dezelfde klant tien keer tonen in een dashboard hoeft niet tien tokens te kosten.
Velden mappen naar CRM of factuurformulier
| Doelveld | Bron in het antwoord |
|---|---|
| Bedrijfsnaam | name |
| Handelsnamen | tradingNames[] |
| Ondernemingsnummer | cbeNumberFormatted |
| Btw-nummer | "BE" + cbeNumber, bevestigd via vat.valid |
| Rechtsvorm | legalFormShort (BV, NV) of legalForm |
| Adres | address.street, houseNumber, postalCode, city |
| Status | isActive, juridicalSituation |
| Hoofdactiviteit | activities[] waar isMain waar is |
Let op bij entityType: "natural_person": een eenmanszaak kan een privéadres als vestigingsadres hebben. Toon dat niet zomaar in publieke context en behandel het volgens de AVG. Het verschil tussen ondernemingsnummer en btw-nummer leg je uit in deze gids, en de VIES-controle in deze.
Patroon: autofill in een checkout
Een gangbaar patroon voor B2B-checkouts:
- De klant typt een ondernemingsnummer of btw-nummer.
- Je front-end normaliseert en controleert het controlegetal lokaal (geen oproep nodig).
- Bij een geldig nummer doet je backend de lookup. De API-sleutel blijft op de server.
- Je vult naam, adres en rechtsvorm in als voorstel, de klant bevestigt of past aan.
- Je bewaart het antwoord met tijdstip bij de bestelling.
Heeft de klant enkel een naam, laat dan een search met postcode een korte lijst tonen. Wil je ook controleren of het bedrijf nog actief is, dan vind je de checklist in de gids over klant- en leveranciersonderzoek. Je kan het mechanisme ook eerst uitproberen met de gratis tool /be/ondernemingsnummer-opzoeken.
Tokens in de hand houden
- Valideer lokaal vooraf, cache in je eigen opslag, dedupliceer batches.
- Zet een teller per dag of per klant, en log
429-antwoorden. - Ontwerp je flow zodat een tijdelijke fout (502, 503) de gebruiker niet blokkeert: laat handmatige invoer toe en verrijk later.
Meer over de Belgische kant van het product lees je op de landingspagina.