EU VAT number validation
Normalises the country code and the number (spaces, punctuation, a repeated prefix, GR → EL), checks the national structure and check digit, then asks VIES (REST first, SOAP fallback). Returns the registered name and address where the member state publishes them, and a consultation number when requester is supplied (prepaid credits or the monthly plan). When VIES or the member state is down, the last answer we hold for that number is returned with source.kind = "cache" and source.stale = true; it is never presented as live. A structurally impossible number answers valid: false without calling VIES and is not counted.
About the upstream
VIES (VAT Information Exchange System) is the European Commission’s relay to the 27 national VAT registers. We call its REST endpoint first and its SOAP service when REST fails; a member state that does not answer gives MS_UNAVAILABLE, which this API turns into a cached answer (marked stale) or a 503. Some states publish no name or address ("---"): the fields are then null. Northern Ireland (XI) is in VIES for goods only; GB is not aliased to XI and answers country_unknown: check UK numbers with HMRC’s free service (gov.uk/check-uk-vat-number), Northern Ireland goods traders send XI. The national check digit is verified for all 28 formats before VIES is asked.
/vat/validateValidate an EU VAT number
Normalises the country code and the number (spaces, punctuation, a repeated prefix, GR → EL), checks the national structure and check digit, then asks VIES (REST first, SOAP fallback). Returns the registered name and address where the member state publishes them, and a consultation number when requester is supplied (prepaid credits or the monthly plan). When VIES or the member state is down, the last answer we hold for that number is returned with source.kind = "cache" and source.stale = true; it is never presented as live. A structurally impossible number answers valid: false without calling VIES and is not counted.
- Tiers
- anonymousfreecreditsplan
- Cost
- 1 call when VIES or the cache answered; 0 when the structure already fails or when a 5xx is returned
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| country | query | string | ISO 3166-1 alpha-2 of the member state; Greece is EL (GR accepted), Northern Ireland XI. May be omitted when number carries the prefix.Example: DE |
| number* | query | string | The VAT number, with or without the country prefix, spaces or punctuation.Example: 136695976 |
| requester | query | string | Your own EU VAT number (with prefix). VIES then issues a consultation number, the evidence a tax audit accepts. Prepaid credits or the monthly plan.Example: FR40303265045 |
| record | query | boolean | true keeps this validation as a record on your account for 10 years (or until you delete it), and the answer gains record. A structure-only answer is not kept (record is null), since VIES was not consulted. A record is our dated copy of the answer; only the consultation number is issued by VIES. Prepaid credits or the monthly plan. |
Request
curl "https://veriport.io/api/v1/vat/validate?country=DE&number=136695976"Response 200
The validation result, live or from the dated cache.
| Field | Type | Description |
|---|---|---|
| valid* | boolean | True when VIES (or the cached VIES answer) says the number is registered for intra-EU transactions on requestDate. |
| countryCode* | string | |
| vatNumber* | string | The national part, normalised. |
| full* | string | Prefix + number. |
| structure* | object | |
| structure.valid | boolean | |
| structure.checksum | "passed" | "failed" | "not_checked" | |
| structure.expected | string | |
| name | string | null | Registered name, when the member state publishes it (some answer "---"). |
| address | string | null | |
| requestDate | string | null (date) | The date VIES stamped the answer with. |
| record | object | null | Present when record=true was sent: the kept record, or null for a structure-only answer. |
| record.id | string | |
| record.createdAt | string (date-time) | |
| record.digest | string | |
| consultationNumber | string | null | VIES request identifier, issued only when requester is supplied and accepted. Keep it with the invoice. |
| source* | object | |
| source.kind | "live" | "cache" | "structure_only" | |
| source.checkedAt | string (date-time) | |
| source.cacheAgeSeconds | integer | |
| source.stale | boolean | True whenever the answer did not come from VIES during this request. |
| upstream* | object | |
| upstream.service | "vies" | |
| upstream.transport | "rest" | "soap" | null | |
| upstream.status | "ok" | "member_state_unavailable" | "service_unavailable" | "timeout" | "blocked" | "not_called" | |
| upstream.memberStateAvailable | boolean | null | |
| tableVersion | string | null | Unused for validation; present for symmetry with the rates endpoints. |
{
"valid": true,
"countryCode": "DE",
"vatNumber": "136695976",
"full": "DE136695976",
"structure": {
"valid": true,
"checksum": "passed",
"expected": "9 digits"
},
"name": null,
"address": null,
"requestDate": "2026-10-24",
"consultationNumber": null,
"source": {
"kind": "live",
"checkedAt": "2026-10-24T09:14:03Z",
"cacheAgeSeconds": 0,
"stale": false
},
"upstream": {
"service": "vies",
"transport": "rest",
"status": "ok",
"memberStateAvailable": true
},
"tableVersion": null
}Headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy, X-Quota-Remaining, X-Tier.
Errors
- 400
invalid_parameter— A parameter is missing or malformed. - 401
unauthorized— The API key is missing, unknown or revoked. - 402
quota_exceeded— Monthly allowance and credits are used up. - 403
forbidden_for_tier— This option is not included in your tier. - 429
rate_limited— Too many requests per second. - 503
vies_unavailable— VIES or the member state is unavailable and no cached answer exists.vies_blocked— VIES refused the request (concurrency or blocking).
/vat/validate/bulkValidate up to 100 EU VAT numbers in one request
Monthly plan only. Each item is processed as GET /vat/validate would, with a bounded concurrency towards VIES; items are answered in order. Each item counts one call. Up to 100 items.
- Tiers
- plan
- Cost
- 1 call per item
Request body
Media types: application/json.
| Field | Type | Description |
|---|---|---|
| items* | object[] | |
| requester | string | Applied to every item. |
{
"items": [
{
"country": "DE",
"number": "136695976"
},
{
"number": "FR40303265045"
}
]
}Request
curl -X POST -H "Authorization: Bearer vp_live_YOUR_KEY" -H "Content-Type: application/json" \
-d '{"items":[{"country":"DE","number":"136695976"},{"number":"FR40303265045"}]}' \
"https://veriport.io/api/v1/vat/validate/bulk"Response 200
One result per item, in order.
| Field | Type | Description |
|---|---|---|
| results | object[] | |
| counted | integer |
Headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy, X-Quota-Remaining, X-Tier.
Errors
- 400
invalid_parameter— A parameter is missing or malformed. - 401
unauthorized— The API key is missing, unknown or revoked. - 402
quota_exceeded— Monthly allowance and credits are used up. - 403
forbidden_for_tier— This option is not included in your tier. - 429
rate_limited— Too many requests per second.
/vat/validationsList the validation records kept on your account
The records made with record=true, newest first, on every key of the account (a rotated key keeps them). Open to every key, a lapsed plan included, and not counted. format=csv returns the same page as CSV with the next cursor in X-Next-Cursor.
- Tiers
- freecreditsplan
- Cost
- free
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| number | query | string | Only this VAT number, with or without its prefix (country supplies a missing prefix). |
| country | query | string | Two-letter prefix when number has none. |
| from | query | string | Records made at or after this ISO 8601 date or instant. |
| to | query | string | Records made before this ISO 8601 date or instant. |
| limit | query | integer | Records per page. |
| cursor | query | string | The nextCursor of the previous page. |
| format | query | "json" | "csv" | The answer format. |
Request
curl -H "Authorization: Bearer vp_live_YOUR_KEY" "https://veriport.io/api/v1/vat/validations"Response 200
One page of records.
Media types: application/json, text/csv.
| Field | Type | Description |
|---|---|---|
| records* | ValidationRecord[] | |
| nextCursor* | string | null |
Headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy, X-Quota-Remaining, X-Tier.
Errors
- 400
invalid_parameter— A parameter is missing or malformed. - 401
unauthorized— The API key is missing, unknown or revoked. - 429
rate_limited— Too many requests per second.
/vat/validations/{id}Get one validation record
One record of your account, as it was kept. Open to every key and not counted.
- Tiers
- freecreditsplan
- Cost
- free
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | The record id (vr_…). |
Request
curl -H "Authorization: Bearer vp_live_YOUR_KEY" "https://veriport.io/api/v1/vat/validations/id"Response 200
The record.
| Field | Type | Description |
|---|---|---|
| id* | string | The record id (vr_…). |
| createdAt* | string (date-time) | When the record was made. |
| keyPrefix | string | The prefix of the key that made it. |
| countryCode | string | |
| vatNumber | string | |
| full* | string | The normalised number with its prefix. |
| valid* | boolean | |
| name | string | null | |
| address | string | null | |
| requestDate | string | null | |
| consultationNumber | string | null | Issued by VIES when requester was supplied. |
| requester | string | null | |
| source* | object | |
| source.kind | "live" | "cache" | |
| source.checkedAt | string (date-time) | |
| source.stale | boolean | |
| upstream* | object | |
| upstream.service | "vies" | |
| upstream.status | string | |
| upstream.transport | "rest" | "soap" | null | |
| digest* | string | SHA-256 (hex) of the JSON array [id, createdAt, full, valid, name, address, requestDate, consultationNumber, requester, source.kind, source.checkedAt, source.stale, upstream.status, upstream.transport]. Store it to compare the record later. |
Headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy, X-Quota-Remaining, X-Tier.
Errors
- 401
unauthorized— The API key is missing, unknown or revoked. - 404
not_found— No such resource. - 429
rate_limited— Too many requests per second.
/vat/validations/{id}Delete one validation record
Deletes the record for good. Records cannot be edited, only deleted. Open to every key and not counted.
- Tiers
- freecreditsplan
- Cost
- free
Parameters
| Name | In | Type | Description |
|---|---|---|---|
| id* | path | string | The record id (vr_…). |
Request
curl -H "Authorization: Bearer vp_live_YOUR_KEY" "https://veriport.io/api/v1/vat/validations/id"Response 200
Deleted.
| Field | Type | Description |
|---|---|---|
| id* | string | |
| deleted* | boolean |
Headers: RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, RateLimit-Policy, X-Quota-Remaining, X-Tier.
Errors
- 401
unauthorized— The API key is missing, unknown or revoked. - 404
not_found— No such resource. - 429
rate_limited— Too many requests per second.
Sources
- VIES REST API — check-vat-number, check-status (technical information) — European Commission, DG TAXUD, consulted 2026-10-24
- VIES SOAP service — checkVatService WSDL — European Commission, DG TAXUD, consulted 2026-10-24
- VIES — help page: what the consultation number proves, member-state availability — European Commission, DG TAXUD, consulted 2026-10-24
- VIES — VAT Information Exchange System: FAQ and VAT number structures (Q11) — European Commission, DG TAXUD, consulted 2026-10-24