How to Look Up KVK Directors and Officers via the API
A practical guide to retrieving directors, officers, and authorized signatories from the Dutch Chamber of Commerce register via the KVK API. Includes code examples for KYC and due diligence.
For business KYC and due diligence, verifying the company itself is only half the picture. You also need to know who is behind it: the directors, officers, and authorized representatives who can act on behalf of the organization. The Dutch Chamber of Commerce register (Handelsregister) contains exactly this information, and the KVK API lets you retrieve it programmatically.
This guide explains how to query directors and officers through the KVK API, what data fields are available, and how to integrate this into your compliance or onboarding workflow.
What the Handelsregister Contains About Officers
For every legal entity (BV, NV, foundation, association), the Handelsregister records the persons authorized to act on behalf of the company. This typically includes:
- Directors/executives: individuals responsible for day-to-day management
- Supervisory board members: commissioners in larger BVs
- Proxy holders: individuals with a commercial power of attorney
- Other officers: additional registered authorized persons
For each officer, the register contains name, partial date of birth, job title, and the type of authority (sole, joint, or limited).
Fetching Officers via the KVKBase API
With the KVKBase API, officer data is returned as part of the company object in the officers field:
import requests
API_KEY = "your_api_key"
def get_officers(kvk_number: str) -> list:
response = requests.get(
f"https://api.kvkbase.nl/v1/company/{kvk_number}",
headers={"Authorization": f"Bearer {API_KEY}"}
)
response.raise_for_status()
data = response.json()
return data.get("officers", [])
# Example usage
officers = get_officers("12345678")
for officer in officers:
print(f"{officer['name']} - {officer['title']} ({officer['authorityType']})")
A typical response looks like this:
{
"officers": [
{
"name": "J. de Vries",
"title": "Director",
"authorityType": "sole",
"dateOfBirth": "1980-04",
"startDate": "2019-04-15",
"endDate": null
},
{
"name": "M. Jansen",
"title": "Supervisory Director",
"authorityType": "none",
"dateOfBirth": "1965-11",
"startDate": "2021-01-01",
"endDate": null
}
]
}
Note: the KVK publishes dates of birth without the day, only year and month. This is a deliberate privacy measure in the Handelsregister.
Authority Types: Sole vs. Joint
The authorityType field indicates how the officer is authorized to act:
sole: may sign and commit the company independentlyjoint: may only act together with another authorized officerlimited: authority is restricted to a specific amount or type of transactionnone: no signing authority (e.g. supervisory board members)
This matters when you are signing contracts. With joint authority, a contract must be signed by two directors to be legally binding.
def check_signing_authority(kvk_number: str, name: str) -> dict:
officers = get_officers(kvk_number)
for officer in officers:
if name.lower() in officer["name"].lower() and officer["endDate"] is None:
return {
"found": True,
"name": officer["name"],
"title": officer["title"],
"authority": officer["authorityType"],
"can_sign_alone": officer["authorityType"] == "sole"
}
return {"found": False}
Historical Officers
The Handelsregister also contains historical records: former directors who have been deregistered. In the API, you can identify these by a non-null endDate. For due diligence, this is useful: a former director who left before a legal dispute, or a board that changed recently, may warrant additional investigation.
def split_active_and_historical(officers: list) -> dict:
active = [o for o in officers if o.get("endDate") is None]
historical = [o for o in officers if o.get("endDate") is not None]
return {
"active": active,
"historical": historical,
"recently_changed": any(
o.get("startDate", "") >= "2026-01-01" or
o.get("endDate", "") >= "2026-01-01"
for o in officers
)
}
Integrating Officers into a KYC Workflow
In a business onboarding flow, you can combine officer data with other KVK checks for a complete verification process:
def kyc_company(kvk_number: str, claimed_director: str) -> dict:
# 1. Fetch base company data
company_data = get_company_data(kvk_number)
# 2. Check company status
if company_data.get("status") != "active":
return {"approved": False, "reason": "Company is not active"}
# 3. Get officers
officers = company_data.get("officers", [])
active_names = [
o["name"].lower() for o in officers
if o.get("endDate") is None
]
# 4. Verify the claimed director is actually registered
name_match = any(
claimed_director.lower() in name
for name in active_names
)
if not name_match:
return {
"approved": False,
"reason": f"{claimed_director} is not a registered active officer"
}
# 5. Check signing authority
authority = check_signing_authority(kvk_number, claimed_director)
return {
"approved": True,
"company": company_data.get("name"),
"director_verified": True,
"can_sign_alone": authority.get("can_sign_alone", False),
"total_active_officers": len([o for o in officers if o.get("endDate") is None])
}
UBO and Additional Due Diligence
The KVK also maintains the UBO register (Ultimate Beneficial Owners) for certain legal entities. This is a separate register that is not fully public, but partially accessible via API integrations.
For a complete KYC process, you typically combine KVK officer data with:
- UBO register: who are the ultimate owners (shareholders >25%)?
- Sanctions lists: EU/OFAC screening
- PEP lists: politically exposed persons
The KYC compliance guide covers how to set up a full verification process.
Bulk Officer Lookups
For verifying multiple companies at once, use batch processing with proper rate limit handling:
import time
def batch_officers(kvk_numbers: list, delay: float = 0.1) -> dict:
results = {}
for kvk in kvk_numbers:
try:
results[kvk] = get_officers(kvk)
time.sleep(delay)
except requests.HTTPError as e:
if e.response.status_code == 429:
time.sleep(5)
results[kvk] = get_officers(kvk)
else:
results[kvk] = {"error": str(e)}
return results
See the batch enrichment guide for more patterns when working with company data at scale.
Summary
Officer data from the Dutch Chamber of Commerce register is valuable for compliance, KYC, and contract management. Via the KVKBase API, you retrieve this data as part of the standard company lookup without a separate integration. Combining company status, active directors, and authority type gives you a solid foundation for automated business verification.
Ready to try it yourself? Create a free KVKBase account and start querying with your own KVK numbers.