API Reference

Base URL https://api.kvkbase.nl/v1

Authentication

All endpoints (except /v1/health) require an API key. Two methods are supported:

Query Parameter

?apikey=YOUR_API_KEY

Get your API key from the Dashboard. Keys are scoped to your account tier and rate limits.

GET /v1/lookup/{kvkNumber}

Company Lookup

Look up detailed company information by KVK number. Returns company name, address, legal form, activities, and VIES-validated VAT number in a single call. The vat object carries a status of validated, stale, malformed or unvalidated, so a VIES outage never reaches you as a false negative. See the Error Codes page for what each one means.

Parameters

kvkNumber required
string
8-digit KVK number (path parameter)
vat
boolean
Set to true to fetch the KVK Basisprofiel: VAT number (validated through VIES), employee counts, statutory name, formal registration date and the official SBI descriptions. Costs 12 tokens instead of 2. The VAT number is derived from the RSIN, so it is only returned for legal persons such as a BV or NV; a sole trader or partnership has no RSIN in the register and gets vat: null. The other fields are returned for every legal form.

Request

curl -X GET "https://api.kvkbase.nl/v1/lookup/12345678" \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.kvkbase.nl/v1/lookup/12345678",
  {
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
    },
  }
);
const company = await response.json();
console.log(company.name); // "Acme B.V."
import requests

response = requests.get(
    "https://api.kvkbase.nl/v1/lookup/12345678",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
company = response.json()
print(company["name"])  # "Acme B.V."
$ch = curl_init("https://api.kvkbase.nl/v1/lookup/12345678");
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer YOUR_API_KEY"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$response = json_decode(curl_exec($ch));
echo $response->name; // "Acme B.V."
req, _ := http.NewRequest("GET",
    "https://api.kvkbase.nl/v1/lookup/12345678", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")

resp, _ := http.DefaultClient.Do(req)
defer resp.Body.Close()

var company map[string]interface{}
json.NewDecoder(resp.Body).Decode(&company)
fmt.Println(company["name"]) // "Acme B.V."
require "net/http"
require "json"

uri = URI("https://api.kvkbase.nl/v1/lookup/12345678")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"

res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http|
  http.request(req)
}
company = JSON.parse(res.body)
puts company["name"] # "Acme B.V."

Response

{
  "kvkNumber": "12345678",
  "name": "Acme B.V.",
  "tradingNames": ["Acme", "Acme Trading"],
  "legalForm": "BV",
  "address": {
    "street": "Herengracht",
    "houseNumber": "100",
    "postalCode": "1015 AA",
    "city": "Amsterdam",
    "country": "NL"
  },
  "isActive": true,
  "registrationDate": "2020-01-15",
  "activities": [
    { "sbiCode": "6201", "description": "Ontwikkelen en uitgeven van software", "isMain": true }
  ],
  "vat": {
    "number": "NL123456789B01",
    "valid": true,
    "status": "validated",
    "validatedAt": "2026-03-15T12:00:00Z",
    "checksumValid": true
  },
  "source": {
    "kvk": "opendata+zoeken",
    "vies": "validated",
    "cachedAt": "2026-03-15T12:00:00Z"
  }
}
GET /v1/validate/vat/{vatNumber}

VAT Validation

Validate a Dutch or EU VAT number against the VIES system. Returns validation status and registered company details. When a member state service is down, status tells you whether you are looking at a live verdict (validated), a dated one we obtained earlier (stale, with ageSeconds), or a number the Dutch checksum rejects outright (malformed). A 502 UPSTREAM_ERROR is reserved for when we have none of those. checksumValid is the Dutch elfproef: it means well formed, never valid.

Parameters

vatNumber required
string
VAT number, e.g. NL123456789B01 (path parameter)

Request

curl -X GET "https://api.kvkbase.nl/v1/validate/vat/NL123456789B01" \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.kvkbase.nl/v1/validate/vat/NL123456789B01",
  { headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
const { valid, name } = await response.json();
response = requests.get(
    "https://api.kvkbase.nl/v1/validate/vat/NL123456789B01",
    headers={"Authorization": "Bearer YOUR_API_KEY"}
)
result = response.json()
print(f"Valid: {result['valid']}")
$ch = curl_init(
    "https://api.kvkbase.nl/v1/validate/vat/NL123456789B01"
);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer YOUR_API_KEY"
]);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$result = json_decode(curl_exec($ch));
req, _ := http.NewRequest("GET",
    "https://api.kvkbase.nl/v1/validate/vat/NL123456789B01", nil)
req.Header.Set("Authorization", "Bearer YOUR_API_KEY")
resp, _ := http.DefaultClient.Do(req)
uri = URI("https://api.kvkbase.nl/v1/validate/vat/NL123456789B01")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer YOUR_API_KEY"

Response

{
  "vatNumber": "NL123456789B01",
  "valid": true,
  "name": "Acme B.V.",
  "address": "Herengracht 100, 1015 AA Amsterdam",
  "validatedAt": "2026-03-15T12:00:00Z",
  "status": "validated",
  "checksumValid": true
}
GET /v1/be/lookup/{cbeNumber}

Belgium: Company Lookup

Look up a Belgian company by enterprise number (ondernemingsnummer). Any common notation works: 0821.015.819, BE0821015819 or 821015819. The same lookup is also reachable as GET /v1/lookup/{number}. Returns name, trade names, legal form, address, status, NACE activities (in the sbiCode field, with the version in naceVersion), establishments and the VAT status in one call. The vat object works like the Dutch one: validated is a verdict from VIES or its cache, stale is an older verdict from when VIES was down, unvalidated means we have no verdict and valid is null. A verdict we do not have is never reported as false. Answers can be up to a week old. When we cannot refresh a record, for example because our data allowance for the day is used up, we serve the stored copy and set source.stale to true. Costs 1 token.

Parameters

cbeNumber required
string
Enterprise number, 10 digits with a valid check digit pair, in any notation (path parameter)
lang
string
Language of labels such as legal form and activity descriptions: nl, fr or de. Defaults to the Accept-Language header, then nl. en is accepted and answered in Dutch.

Request

curl "https://api.kvkbase.nl/v1/be/lookup/0821.015.819?lang=nl" \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.kvkbase.nl/v1/be/lookup/BE0821015819",
  { headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
const company = await response.json();
console.log(company.name, company.vat?.status); // "INSPACE" "validated"

Response

{
  "country": "BE",
  "cbeNumber": "0821015819",
  "cbeNumberFormatted": "0821.015.819",
  "name": "INSPACE",
  "tradingNames": [],
  "legalForm": "Besloten vennootschap",
  "legalFormCode": "610",
  "legalFormShort": "BV",
  "entityType": "legal_person",
  "address": {
    "street": "Rue des Deux Chaussees",
    "houseNumber": "25",
    "postalCode": "1160",
    "city": "Auderghem",
    "country": "BE"
  },
  "isActive": true,
  "juridicalSituation": "Normale toestand",
  "juridicalSituationCode": "000",
  "registrationDate": "2009-12-01",
  "activities": [
    { "sbiCode": "43990", "description": "Overige gespecialiseerde bouwactiviteiten", "isMain": true },
    { "sbiCode": "64200", "description": "Holdings", "isMain": false }
  ],
  "naceVersion": "Nace2025",
  "establishments": [
    {
      "establishmentNumber": "2183353402",
      "startDate": "2009-12-18",
      "address": { "street": "Rue des Deux Chaussees", "houseNumber": "25", "postalCode": "1160", "city": "Auderghem", "country": "BE" }
    }
  ],
  "vat": {
    "number": "BE0821015819",
    "valid": true,
    "status": "validated",
    "validatedAt": "2026-11-09T09:30:00Z",
    "checksumValid": true
  },
  "enriched": false,
  "source": {
    "cbe": "register",
    "vies": "live",
    "cachedAt": "2026-11-09T09:30:00Z"
  }
}
POST /v1/be/lookup/batch

Belgium: Batch Lookup

Look up up to 25 Belgian companies in one request. Costs 1 token per enterprise number, charged up front. The request fails with 400 INVALID_CBE_NUMBER when any number is invalid. Per company, the result is either success with the same data as a single lookup, or an error object, so one missing company does not fail the batch. A number that appears twice is looked up once.

Parameters

cbeNumbers required
string[]
Array of enterprise numbers, any notation (request body, max 25)
lang
string
nl, fr or de (request body, default: Accept-Language, then nl)

Request

curl -X POST "https://api.kvkbase.nl/v1/be/lookup/batch" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"cbeNumbers": ["0821.015.819", "BE0417497106"]}'
const response = await fetch(
  "https://api.kvkbase.nl/v1/be/lookup/batch",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer YOUR_API_KEY",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      cbeNumbers: ["0821.015.819", "BE0417497106"],
    }),
  }
);
const { results, count, tokensUsed } = await response.json();

Response

{
  "results": {
    "0821015819": { "success": true, "data": { "country": "BE", "cbeNumber": "0821015819", "name": "INSPACE", "...": "same fields as a single lookup" } },
    "0417497106": { "success": false, "error": { "error": { "code": "NOT_FOUND", "message": "Company with enterprise number 0417497106 not found." } } }
  },
  "count": 2,
  "tokensUsed": 2
}
GET /v1/be/validate/vat/{vatNumber}

Belgium: VAT Validation

Validate a Belgian VAT number (BE plus the 10 digit enterprise number, a 9 digit number is accepted too) against VIES. The path with the BE prefix, /v1/validate/vat/BE0821015819, works the same. A number that fails the Belgian check digits is answered as malformed without calling VIES. Status values and the checksumValid field have the same meaning as for Dutch VAT numbers: validated, stale, malformed, and 502 UPSTREAM_ERROR when we have nothing at all.

Parameters

vatNumber required
string
Belgian VAT number, e.g. BE0821015819 (path parameter)

Request

curl "https://api.kvkbase.nl/v1/be/validate/vat/BE0821015819" \
  -H "Authorization: Bearer YOUR_API_KEY"
const response = await fetch(
  "https://api.kvkbase.nl/v1/be/validate/vat/BE0821015819",
  { headers: { Authorization: "Bearer YOUR_API_KEY" } }
);
const { valid, status } = await response.json();

Response

{
  "vatNumber": "BE0821015819",
  "valid": true,
  "validatedAt": "2026-11-09T09:30:00Z",
  "status": "validated",
  "checksumValid": true,
  "country": "BE"
}
GET /v1/health

Health Check

Check API and upstream service status. No authentication required.

Request

curl https://api.kvkbase.nl/v1/health
const response = await fetch("https://api.kvkbase.nl/v1/health");
const health = await response.json();
console.log(health.status); // "ok"
response = requests.get("https://api.kvkbase.nl/v1/health")
print(response.json()["status"])  # "ok"
$health = json_decode(
    file_get_contents("https://api.kvkbase.nl/v1/health")
);
resp, _ := http.Get("https://api.kvkbase.nl/v1/health")
res = Net::HTTP.get(URI("https://api.kvkbase.nl/v1/health"))

Response

{
  "status": "ok",
  "services": {
    "kvk": "up",
    "vies": "up"
  },
  "timestamp": "2026-03-15T12:00:00Z"
}

Belgium notes

The Belgian endpoints above return the same kind of data as the Dutch ones, with these differences.

Topic
Details
Number format
Enterprise numbers have 10 digits and a check digit pair. 0821.015.819, BE0821015819 and 821015819 are the same company. cbeNumber has no separators, cbeNumberFormatted has dots. An invalid number is answered with 400 INVALID_CBE_NUMBER before any token is charged.
Fields
country is always BE. legalForm is the label in the requested language, legalFormCode the register code, legalFormShort the abbreviation (BV, NV and so on). entityType is legal_person or natural_person. isActive is true, false or null when unknown. activities carry NACE codes in sbiCode, naceVersion says which NACE version the codes use.
Language
The lang parameter takes nl, fr or de and decides the language of legal form, juridical situation and activity descriptions. en is accepted and served in Dutch.
Freshness
A company answer can be up to a week old, a search answer up to an hour. When we cannot refresh a record, for example because our data allowance for the day is used up, we serve the stored copy and set source.stale to true (stale on a search). Check source.cachedAt for the age.
VAT status
The vat object has the same semantics as for Dutch companies: validated, stale, malformed or unvalidated, with valid: null when we have no verdict. A VIES outage never shows up as a false negative.
Tokens
1 token per lookup, per search and per company in a batch. VAT validation costs 1 token.
Errors
INVALID_CBE_NUMBER (400) for a malformed number or a failing check digit pair. UPSTREAM_BUDGET (503, with Retry-After) when our data allowance for the day is used up and the record is not stored yet: retry after the number of seconds in the header. NOT_FOUND (404) for an unknown number. See the Error Codes page.

Errors

All errors return a consistent JSON format:

{
  "error": {
    "code": "NOT_FOUND",
    "message": "No company found with KVK number 99999999"
  }
}

See the Error Codes page for a complete reference with HTTP status codes.

Rate Limits

Rate limits are applied per API key based on your plan tier:

Tier
Rate Limit
Headers
free
10 req/min
X-RateLimit-Limit, X-RateLimit-Remaining
starter
60 req/min
X-RateLimit-Limit, X-RateLimit-Remaining
pro
300 req/min
X-RateLimit-Limit, X-RateLimit-Remaining
business
1,000 req/min
X-RateLimit-Limit, X-RateLimit-Remaining

Token Costs

Call
Cost
Notes
Basic lookup
1 token
Name, address, status
Enriched lookup
2 tokens
Trade names, address, legal form, SBI codes and branches (?enrich=true)
Enriched via KVK Basisprofiel
12 tokens
Adds employee counts, statutory name, registration date and official SBI descriptions, plus the VAT number for legal persons (?enrich=true&vat=true)
Search
1 token
Per search request
Branches
5 tokens
All branches for a company
Belgium lookup, search, batch item
1 token
Per lookup, per search request, per company in a batch
Belgium VAT validation
1 token
VIES validation check
Verify
Free
Existence and active status

When rate limited, the API returns 429 Too Many Requests with a Retry-After header.