Validate Dutch BTW numbers (NL VAT)
Validate Dutch BTW (Belasting over de Toegevoegde Waarde) numbers. Dutch VAT numbers include a unique B-sequence that identifies subsidiaries within a group.
Dutch BTW numbers follow a distinctive format: NL + 9 digits + B + 2 digits (e.g. NL123456789B01). The 9-digit base is the company's RSIN (Rechtspersonen en Samenwerkingsverbanden Informatienummer) from the KvK (Kamer van Koophandel, the Dutch Chamber of Commerce), and the B01/B02 suffix identifies the fiscal unit — B01 is the primary VAT entity, while higher numbers (B02, B03, etc.) indicate subsidiaries within a Dutch fiscal unity (fiscale eenheid). This means two companies with the same RSIN base but different B-suffixes are legally distinct VAT entities.
The Belastingdienst (Dutch Tax and Customs Administration) submits BTW registrations to VIES. Dutch BTW numbers are validated by VIES using a modulus-11 algorithm on the 9-digit RSIN, but the authoritative check for active registration status is always the VIES lookup — do not rely on the checksum alone to accept a number as valid for reverse-charge purposes.
A common integration mistake is stripping the B-suffix before calling the API, which results in a malformed request. Always pass the full NL123456789B01 format including the literal character B and the two-digit suffix to GET /api/v1/validate/NL/:vatNumber. The API returns KvK-linked company_name and company_address when the Belastingdienst node provides them, which you should cross-check against the company name the buyer entered.
Implementation steps
- 1
Handle NLxxxxxxxxx B01 format
Normalise user input by removing spaces and converting to uppercase, then validate with /^NL[0-9]{9}B[0-9]{2}$/. If the user enters the number without the B-suffix (a common mistake), prompt them specifically: 'Dutch BTW numbers end in B followed by two digits, e.g. NL123456789B01.' Do not attempt to auto-append B01 — the correct suffix is determined by the company's fiscal unity status, and guessing it would produce a different VAT entity.
- 2
Validate 9-digit base + B + 2 digits suffix
After format normalisation, call GET /api/v1/validate/NL/:vatNumber with the full string including the B-suffix. Optionally perform a client-side modulus-11 checksum on the 9-digit RSIN (as specified by the Belastingdienst) before the API call to reject obviously invalid numbers instantly; however, pass all format-valid numbers to the API regardless of the checksum result, since VIES is the authoritative source.
- 3
Check against Dutch Tax and Customs Administration
The TaxID API forwards the request to the Belastingdienst's VIES node. Response times are typically under 500 ms. The Belastingdienst node is generally reliable but can return service_unavailable during the first business days of January when VAT registration systems are under higher load. Implement a 3-retry policy with 30-second delays for service_unavailable responses to handle these transient outages.
- 4
Return KvK-linked company data
TaxID returns the company_name and company_address from the VIES response for Dutch numbers. The company_name is the trade name registered with the Belastingdienst, which may differ from the KvK-registered trade name for companies operating under a different commercial name. Store both the BTW number and the VIES-confirmed company_name in your customer record to support the name-check requirement for Dutch reverse-charge invoices.
Code example
Node.js
const res = await fetch(
'http://localhost:3000/api/v1/validate/DE/NL123456789B01',
{ headers: { 'Authorization': 'Bearer YOUR_API_KEY' } }
);
const { valid, status, company_name, company_address } = await res.json();
if (valid) {
console.log(`Valid EU business: ${company_name}`);
} else if (status === 'service_unavailable') {
// VIES is temporarily down — retry or allow with manual check
console.log('VIES unavailable — check back in a few minutes');
} else {
console.log('Invalid VAT number — charge local tax rate');
}Python
import requests
res = requests.get(
"http://localhost:3000/api/v1/validate/DE/NL123456789B01",
headers={"Authorization": "Bearer YOUR_API_KEY"}
)
data = res.json()
if data["valid"]:
print(f"Valid: {data['company_name']}")
elif data["status"] == "service_unavailable":
print("VIES temporarily unavailable")
else:
print("Invalid VAT number")API response
The TaxID API returns a consistent JSON response for every validation request:
{
"valid": true,
"status": "active",
"country_code": "DE",
"vat_number": "123456789",
"company_name": "Example GmbH",
"company_address": "Musterstraße 1, 10115 Berlin",
"request_date": "2026-05-10T00:00:00.000Z",
"cached": false,
"request_id": "req_01j..."
}Error handling
The API uses a consistent Stripe-style error format. Always handle service_unavailable separately — VIES has occasional downtime and you should not reject valid customers during outages.
activeVAT number is valid and the business is registered
invalidVAT number format is wrong or not registered in VIES
service_unavailableVIES or the national system is temporarily down — retry later
Country-specific guides
Further reading
Netherlands VAT Number Validation: BTW-nummer Format, VIES, and Code Examples
Dutch BTW numbers have a unique format: NL + 9 digits + B + 2 digits. The literal 'B' is not a separator — it is a manda…
Dutch VAT Rate 2026: Your Complete Compliance Guide
Our 2026 guide clarifies the Dutch VAT rate, covering 21%, 9%, 0% rates, reverse-charge rules, and automating SaaS valid…
Evaluating EU VAT APIs? Compare TaxID vs Vatstack, Vatlayer, Avalara →
Related use cases
EU VAT compliance for SaaS billing
Handle EU VAT for SaaS subscriptions. Validate customer VAT numbers at signup, determine B2B vs B2C ...
DAC7 marketplace seller VAT verification
Verify seller VAT numbers during marketplace onboarding for DAC7 compliance. EU platforms must colle...
Bulk VAT number validation via CSV import
Validate hundreds of EU VAT numbers in batch using parallel API requests. Ideal for CRM data cleansi...