Validate EU VAT Numbers with a REST API
To validate an EU VAT number via REST API, send a GET request to https://api.eurovalidate.com/v1/vat/{vat_number} with your API key in the X-API-Key header. The response includes the validation status, company name, address, and a confidence score indicating data freshness. No SOAP, no XML parsing, no WSDL downloads. One HTTP call, JSON response, under 300ms.
curl Example
The fastest way to test. Replace YOUR_API_KEY with a free key from eurovalidate.com.
Valid VAT number
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.eurovalidate.com/v1/vat/NL820646660B01
Response:
{
"vat_number": "NL820646660B01",
"country_code": "NL",
"status": "valid",
"company_name": "ABN AMRO BANK N.V.",
"company_address": "GUSTAV MAHLERLAAN 00010\n1082PP AMSTERDAM",
"request_id": "req_abc123",
"meta": {
"confidence": "high",
"source": "vies_live",
"cached": false,
"response_time_ms": 247,
"last_verified": "2026-04-08T09:00:00Z",
"upstream_status": "ok"
}
}
Invalid VAT number
curl -H "X-API-Key: YOUR_API_KEY" \
https://api.eurovalidate.com/v1/vat/DE000000000
Response:
{
"vat_number": "DE000000000",
"country_code": "DE",
"status": "invalid",
"company_name": null,
"company_address": null,
"request_id": "req_def456",
"meta": {
"confidence": "high",
"source": "vies_live",
"cached": false,
"response_time_ms": 189,
"upstream_status": "ok"
}
}
Node.js Example
Install the SDK:
npm install @eurovalidate/sdk
import { EuroValidate } from '@eurovalidate/sdk';
const ev = new EuroValidate('YOUR_API_KEY');
const result = await ev.validateVat('FR40303265045');
if (result.status === 'valid') {
console.log(`Company: ${result.company_name}`);
console.log(`Cached: ${result.meta.cached}`);
console.log(`Confidence: ${result.meta.confidence}`);
} else {
console.log(`VAT ${result.vat_number} is ${result.status}`);
}
Python Example
Install the SDK:
pip install eurovalidate
from eurovalidate import Client
client = Client(api_key="YOUR_API_KEY")
result = client.validate_vat("IT00159560366")
if result.status == "valid":
print(f"Company: {result.company_name}") # FERRARI S.P.A.
print(f"Response time: {result.meta.response_time_ms}ms")
else:
print(f"VAT {result.vat_number} is {result.status}")
Unified Validation (VAT + IBAN + EORI in one call)
For checkout flows where you need multiple checks at once:
curl -X POST https://api.eurovalidate.com/v1/validate \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"vat_number": "NL820646660B01",
"iban": "DE89370400440532013000",
"eori_number": "NL810002383"
}'
This returns all three results in a single response, saving you three round trips.
Response Fields Explained
| Field | Type | Description |
|---|---|---|
vat_number |
string | The VAT number as submitted (normalized) |
country_code |
string | ISO 3166-1 alpha-2 (or EL for Greece) |
status |
string |
valid, invalid, error, or unavailable
|
company_name |
string or null | Registered company name from VIES |
company_address |
string or null | Registered address |
meta.confidence |
string |
high, medium, low, or unknown
|
meta.source |
string |
vies_live or vies_cached
|
meta.cached |
boolean | Whether the response came from cache |
meta.response_time_ms |
integer | Time to produce the response |
Latency
| Scenario | Typical Time |
|---|---|
| Cached response (Redis hit) | 1-5 ms |
| Live VIES call | 150-300 ms |
| VIES slow country (DE) | 500-1000 ms |
| IBAN (offline MOD-97) | 65 ms |
Cached responses are served for 24 hours after the first live check. Second and subsequent requests for the same VAT number hit the cache.
Common Pitfalls
Germany and Spain never return company names. These countries redact trader data under national data protection law. Your code should handle company_name: null for DE and ES VAT numbers.
Greece uses EL, not GR. VIES uses EL as the country prefix for Greece, but ISO 3166 uses GR. The API accepts both: EL094019245 and GR094019245 resolve to the same company.
Format validation happens locally. If you send DE12345 (too short for Germany, which requires 9 digits), the API returns HTTP 400 immediately without calling VIES. This saves your quota.
Rate limits are per API key. Free tier: 100 requests/hour. Headers X-RateLimit-Remaining and X-RateLimit-Reset tell you where you stand.
Error Handling
The API uses RFC 7807 Problem Details for errors:
{
"type": "https://eurovalidate.dev/errors/http",
"title": "VAT number for DE must be 9 characters (got 5)",
"status": 400,
"detail": "VAT number for DE must be 9 characters (got 5)..."
}
Handle these status codes:
| Code | Meaning | Action |
|---|---|---|
| 200 | Success (check status field) |
Process result |
| 400 | Bad format | Fix input |
| 401 | Invalid API key | Check key |
| 429 | Rate limit | Wait, retry after Retry-After
|
| 502 | VIES upstream down | Use cached result or retry |
Authentication
Pass your API key in the X-API-Key header:
X-API-Key: ev_live_your_key_here
Get a free key (no credit card): eurovalidate.com
Pricing
| Tier | Price | Requests |
|---|---|---|
| Free | $0 | 100/hour |
| Starter | $19/mo | 5,000/hour |
| Growth | $49/mo | 25,000/hour |
| Scale | $149/mo | 100,000/hour |
Top comments (0)