KVK API with C# and .NET: Querying Dutch Business Data in Your .NET Application
Integrate the KVKBase API into your C# or .NET project. Code examples for HttpClient, dependency injection, error handling and caching — production-ready.
C# and .NET are the standard for enterprise software development in the Netherlands and beyond. From large-scale ERP systems to modern SaaS platforms and microservices, .NET is everywhere. If you work in such an environment and need Dutch business data from the Chamber of Commerce, this guide shows you how to integrate the KVKBase API cleanly in your C# project using modern .NET patterns.
Requirements
- .NET 8 or higher (LTS)
- An API key from KVKBase
No external SDK needed — the KVKBase API works with standard HTTP and returns clean JSON. System.Net.Http.HttpClient and System.Text.Json are all you need.
Step 1: Configuration
Add your API key to appsettings.json. Never hardcode it directly in your source code.
{
"KVKBase": {
"ApiKey": "",
"BaseUrl": "https://api.kvkbase.nl/api/v1"
}
}
Then create a strongly-typed options class:
// KVKBaseOptions.cs
public class KVKBaseOptions
{
public const string SectionName = "KVKBase";
public string ApiKey { get; set; } = string.Empty;
public string BaseUrl { get; set; } = "https://api.kvkbase.nl/api/v1";
}
Register the options in Program.cs:
builder.Services.Configure<KVKBaseOptions>(
builder.Configuration.GetSection(KVKBaseOptions.SectionName));
Step 2: Response Models
Define C# classes that match the API’s JSON response. This gives you full type safety and IDE autocomplete:
// Models/KvkCompany.cs
using System.Text.Json.Serialization;
public class KvkCompany
{
[JsonPropertyName("kvkNummer")]
public string KvkNumber { get; set; } = string.Empty;
[JsonPropertyName("naam")]
public string Name { get; set; } = string.Empty;
[JsonPropertyName("rechtsvorm")]
public string LegalForm { get; set; } = string.Empty;
[JsonPropertyName("actief")]
public bool IsActive { get; set; }
[JsonPropertyName("adres")]
public KvkAddress? Address { get; set; }
[JsonPropertyName("btwNummer")]
public string? VatNumber { get; set; }
[JsonPropertyName("sbiCodes")]
public List<SbiCode> SbiCodes { get; set; } = [];
}
public class KvkAddress
{
[JsonPropertyName("straat")]
public string Street { get; set; } = string.Empty;
[JsonPropertyName("huisnummer")]
public string HouseNumber { get; set; } = string.Empty;
[JsonPropertyName("postcode")]
public string PostalCode { get; set; } = string.Empty;
[JsonPropertyName("plaats")]
public string City { get; set; } = string.Empty;
}
public class SbiCode
{
[JsonPropertyName("code")]
public string Code { get; set; } = string.Empty;
[JsonPropertyName("omschrijving")]
public string Description { get; set; } = string.Empty;
}
Step 3: Register HttpClient
The recommended approach in .NET is a typed or named HttpClient via IHttpClientFactory:
// Program.cs
builder.Services.AddHttpClient("KVKBase", (sp, client) =>
{
var options = sp.GetRequiredService<IOptions<KVKBaseOptions>>().Value;
client.BaseAddress = new Uri(options.BaseUrl);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer {options.ApiKey}");
client.Timeout = TimeSpan.FromSeconds(10);
});
Step 4: KVKBase Service
Create a service class with dependency injection. This keeps your controllers and handlers clean:
// Services/KvkBaseService.cs
using System.Net.Http.Json;
using Microsoft.Extensions.Options;
public interface IKvkBaseService
{
Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default);
Task<List<KvkCompany>> SearchCompaniesAsync(string query, CancellationToken ct = default);
}
public class KvkBaseService : IKvkBaseService
{
private readonly HttpClient _http;
private readonly ILogger<KvkBaseService> _logger;
public KvkBaseService(IHttpClientFactory factory, ILogger<KvkBaseService> logger)
{
_http = factory.CreateClient("KVKBase");
_logger = logger;
}
public async Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default)
{
try
{
var response = await _http.GetAsync($"/api/v1/lookup/{kvkNumber}", ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<KvkCompany>(cancellationToken: ct);
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning("KVK number {KvkNumber} not found", kvkNumber);
return null;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error fetching company data for {KvkNumber}", kvkNumber);
throw;
}
}
public async Task<List<KvkCompany>> SearchCompaniesAsync(string query, CancellationToken ct = default)
{
var url = $"/api/v1/search?q={Uri.EscapeDataString(query)}";
var response = await _http.GetAsync(url, ct);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadFromJsonAsync<KvkSearchResult>(cancellationToken: ct);
return result?.Items ?? [];
}
}
public class KvkSearchResult
{
[System.Text.Json.Serialization.JsonPropertyName("items")]
public List<KvkCompany> Items { get; set; } = [];
}
Register the service in Program.cs:
builder.Services.AddScoped<IKvkBaseService, KvkBaseService>();
Step 5: Using the Service in a Controller
Use the service in a Minimal API endpoint or MVC controller:
// Minimal API example
app.MapGet("/api/company/{kvkNumber}", async (
string kvkNumber,
IKvkBaseService kvkService,
CancellationToken ct) =>
{
var company = await kvkService.GetCompanyAsync(kvkNumber, ct);
return company is null ? Results.NotFound() : Results.Ok(company);
})
.WithName("GetCompany")
.WithOpenApi();
For MVC-style:
[ApiController]
[Route("api/[controller]")]
public class CompanyController : ControllerBase
{
private readonly IKvkBaseService _kvk;
public CompanyController(IKvkBaseService kvk) => _kvk = kvk;
[HttpGet("{kvkNumber}")]
public async Task<ActionResult<KvkCompany>> Get(string kvkNumber, CancellationToken ct)
{
var company = await _kvk.GetCompanyAsync(kvkNumber, ct);
return company is null ? NotFound() : Ok(company);
}
}
Caching with IMemoryCache
KVK data rarely changes. Add in-memory caching to reduce API costs and lower latency:
public class KvkBaseService : IKvkBaseService
{
private readonly HttpClient _http;
private readonly IMemoryCache _cache;
private readonly ILogger<KvkBaseService> _logger;
private static readonly TimeSpan CacheDuration = TimeSpan.FromHours(24);
public KvkBaseService(IHttpClientFactory factory, IMemoryCache cache, ILogger<KvkBaseService> logger)
{
_http = factory.CreateClient("KVKBase");
_cache = cache;
_logger = logger;
}
public async Task<KvkCompany?> GetCompanyAsync(string kvkNumber, CancellationToken ct = default)
{
var cacheKey = $"kvk:{kvkNumber}";
if (_cache.TryGetValue(cacheKey, out KvkCompany? cached))
return cached;
var company = await FetchFromApiAsync(kvkNumber, ct);
if (company is not null)
_cache.Set(cacheKey, company, CacheDuration);
return company;
}
private async Task<KvkCompany?> FetchFromApiAsync(string kvkNumber, CancellationToken ct)
{
try
{
var response = await _http.GetAsync($"/api/v1/lookup/{kvkNumber}", ct);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<KvkCompany>(cancellationToken: ct);
}
catch (HttpRequestException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
_logger.LogWarning("KVK number {KvkNumber} not found", kvkNumber);
return null;
}
}
}
Register IMemoryCache in Program.cs:
builder.Services.AddMemoryCache();
VAT Number Validation
KVKBase also supports VAT number validation via VIES. Here’s how to do it in C#:
public async Task<bool> ValidateVatNumberAsync(string vatNumber, CancellationToken ct = default)
{
var response = await _http.GetAsync(
$"/api/v1/vat/validate/{Uri.EscapeDataString(vatNumber)}", ct);
if (!response.IsSuccessStatusCode)
return false;
var result = await response.Content.ReadFromJsonAsync<VatValidationResult>(cancellationToken: ct);
return result?.IsValid ?? false;
}
public class VatValidationResult
{
[System.Text.Json.Serialization.JsonPropertyName("valid")]
public bool IsValid { get; set; }
}
Common Mistakes to Avoid
Instantiating HttpClient directly: Never use new HttpClient() inside a service class. Always use IHttpClientFactory. Direct instantiation causes socket exhaustion under load.
API key in source code: Always use appsettings.json combined with environment variables or Azure Key Vault. Never hardcode an API key.
No timeout set: An external API can respond slowly. Always set a timeout on your HttpClient (the default 100 seconds is far too long for a lookup call).
Not passing CancellationToken: In ASP.NET Core you always have a CancellationToken from the request scope. Pass it through — it automatically cancels in-flight HTTP calls when the client disconnects.
Next Steps
Now that you’ve integrated the KVKBase API, there’s more to explore:
- KVK number validation rules and checksum — learn how the 11-test algorithm works
- Batch enrichment of CRM data — loop through thousands of companies and enrich your database
- VAT number validation via VIES — complete guide to European VAT validation
Get a free API key at kvkbase.nl and start building today.