VIES VAT validation is the European Commission’s free check of whether an EU VAT number is valid for cross-border trade. You send a country code and a number, VIES forwards the question to the tax administration that issued the number, and you get its answer back: valid or invalid, plus the registered name and address where that country shares them. This guide explains what that answer means, why checks fail, how UK numbers fit in since Brexit, and how to call VIES from code. The behaviour and error codes below were checked against the live VIES service on 29 October 2026.
General information, not tax advice.
1. What VIES is
VIES (VAT Information Exchange System) connects the VAT registers of the 27 EU member states and Northern Ireland. There is no central EU database behind it. In the words of the VIES FAQ: “Each time you make a request, a message is sent to the Member State which issued the VAT identification number asking if the number quoted is valid.”
That has two consequences:
- The answer is current. VIES shows the national register’s state at the moment you ask.
- The answer depends on one national system. If that system is down, numbers from that country cannot be checked at all (see section 4).
VIES exists so that businesses can confirm their trading partners’ VAT numbers (Article 31 of Regulation (EU) No 904/2010). Its disclaimer forbids other uses, such as extracting or republishing the data.
2. What a VIES check returns
The REST API’s check-vat-number answer has these fields:
| Field | What it means |
|---|---|
valid | true if the number is in the issuing country’s VIES register right now |
name, address | The registered name and address, only where the member state shares them. Germany and Spain return --- |
requestDate | When VIES answered |
requestIdentifier | The consultation number, if you sent your own VAT number as the requester; otherwise empty |
traderNameMatch and the other …Match fields | VALID, INVALID or NOT_PROCESSED, if you sent the name and address you expect (approximate matching). Most member states answer NOT_PROCESSED |
When VIES says “invalid”
valid: false does not always mean a typo. According to the VIES FAQ, the number may not exist, may not be activated for intra-EU transactions (some member states keep a separate register for that), or the registration may not be complete yet. Registrations also end.
A correct check digit is no proof either. In our test, DE136695976, a number with a valid German check digit that appears in many code examples, came back as invalid: the checksum shows the number is well formed, not that it is registered.
If a customer’s number comes back invalid, ask them to check it. If it is correct, they have to contact their own tax administration, because only national administrations can update VIES. Until the number is confirmed, don’t invoice without VAT. The reverse charge guide explains why.
3. The consultation number
Enter your own EU VAT number as the requester and the answer contains a unique consultation number (requestIdentifier in the REST API). The VIES help page recommends keeping it “if you want to be able to prove to a tax administration of a Member State that you have checked a given VAT number at a given time”.
- It is free and costs no extra request.
- Your requester number must itself be a valid EU or XI VAT number; otherwise VIES answers
INVALID_REQUESTER_INFO. - There is none for GB numbers, which VIES does not check.
- VIES calls it “only ONE of the elements of evidence” for a VAT-free intra-EU supply. Keep the transport documents and the rest of your records too.
4. Why VIES checks fail
VIES reports problems in an errorWrappers list. The REST API sends most of them with HTTP status 200, so code that only looks at the status code treats a failed check as an answer. A missing field is the exception: HTTP 400 with VOW-ERR-11.
| Error | Meaning | What to do |
|---|---|---|
INVALID_INPUT | Unknown country code (GB, for example) or an empty number | Fix the input; a retry gives the same answer |
INVALID_REQUESTER_INFO | Your requester VAT number is not valid | Fix your own number |
MS_UNAVAILABLE | The member state’s system is not answering, often during maintenance | Retry later |
TIMEOUT | No answer in time | Retry later |
SERVICE_UNAVAILABLE | VIES itself or the network failed | Retry later |
MS_MAX_CONCURRENT_REQ, GLOBAL_MAX_CONCURRENT_REQ (and …_TIME variants) | Too many requests at once, for one member state or overall | Slow down, then retry |
VAT_BLOCKED, IP_BLOCKED | The number or your IP address is blocked | Contact the VIES help desk |
Maintenance windows
National systems have regular downtimes. A few from the VIES help page (times as VIES lists them; it gives no time zone):
| Country | Availability |
|---|---|
| Germany | Available from 05:00 to 23:00 |
| Netherlands | Unavailable every weekend from Saturday 21:50 to Sunday 21:40 |
| Italy | Unavailable Monday to Saturday from 20:00 for 30 to 60 minutes |
| Romania | Unavailable almost every weekend from Saturday 21:50 to Sunday 21:50 |
| Northern Ireland (XI) | Unavailable Saturdays from 07:30 to 10:30 and almost daily from about 04:30 to 04:40 |
The check-status endpoint shows which countries VIES can reach right now.
When a check fails: don’t reject the customer. Take the order, mark the number as unchecked, retry later and keep the error with its time as a record that you tried. A format and check-digit test still catches typos in the meantime (see VAT number formats).
5. UK VAT numbers since Brexit
GB numbers left VIES on 1 January 2021; VIES now answers INVALID_INPUT for them. Northern Ireland businesses that trade goods with the EU use their VAT number with the XI prefix, and those numbers are checked in VIES like any EU number.
GB numbers are checked with HMRC: in the Check a UK VAT number service on GOV.UK, or through HMRC’s API. Version 2 of that API needs an application registered with HMRC’s Developer Hub; version 1, which worked without one, was removed on 17 February 2025. The free UK VAT checker checks GB and XI numbers without code.
6. Call VIES from code
| Interface | Address |
|---|---|
| REST: check a number | POST https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number |
| REST: status of each member state | GET https://ec.europa.eu/taxation_customs/vies/rest-api/check-status |
| REST: test service | POST https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-test-service |
| SOAP | https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl (operations checkVat and checkVatApprox) |
None of them needs a key. The REST request body takes countryCode and vatNumber (without the prefix), and optionally requesterMemberStateCode and requesterNumber for a consultation number.
This Python function (Python 3.11, requests 2.34) normalises the input, sends the check and retries only the errors that can go away:
import os
import time
import requests
VIES_URL = "https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number"
# VIES reports these in an errorWrappers list, with HTTP status 200. Retry them later.
RETRYABLE = {
"SERVICE_UNAVAILABLE", "MS_UNAVAILABLE", "TIMEOUT", "SERVER_BUSY",
"GLOBAL_MAX_CONCURRENT_REQ", "GLOBAL_MAX_CONCURRENT_REQ_TIME",
"MS_MAX_CONCURRENT_REQ", "MS_MAX_CONCURRENT_REQ_TIME",
}
class ViesError(Exception):
def __init__(self, code):
super().__init__(code)
self.code = code
self.retryable = code in RETRYABLE
def check_vies(vat_number, requester=None, attempts=3, url=VIES_URL):
"""Check an EU VAT number such as 'LU26375245' in VIES.
requester: your own EU VAT number. VIES then returns a consultation
number in requestIdentifier.
"""
vat = vat_number.upper().replace(" ", "").replace(".", "").replace("-", "")
body = {"countryCode": vat[:2], "vatNumber": vat[2:]}
if requester:
body["requesterMemberStateCode"] = requester[:2].upper()
body["requesterNumber"] = requester[2:]
for attempt in range(1, attempts + 1):
try:
response = requests.post(url, json=body, timeout=30)
data = response.json()
except (requests.RequestException, ValueError):
data = {"errorWrappers": [{"error": "SERVICE_UNAVAILABLE"}]}
errors = data.get("errorWrappers") or []
if not errors and "valid" in data:
return data
error = ViesError(errors[0].get("error", "UNKNOWN") if errors else "UNKNOWN")
if not error.retryable or attempt == attempts:
raise error
time.sleep(5 * attempt) # wait 5 s, then 10 s
if __name__ == "__main__":
result = check_vies("LU26375245", requester=os.environ.get("MY_VAT_NUMBER"))
print(result["valid"], result["name"], result["requestIdentifier"] or "-")
What it did in our test run against the live service:
check_vies("lu 2637-5245")returnedvalid: Truewith the nameAMAZON EUROPE CORE S.A R.L.; with a requester it also returned a consultation number starting withWAPI.check_vies("DE136695976")returnedvalid: False.check_vies("GB980780684")raisedViesError("INVALID_INPUT")without retrying.- An unreachable server ended in
SERVICE_UNAVAILABLEafter three attempts.
Test your error handling
The test service answers by the number you send: 100 is valid and 200 invalid, and 201, 202, 300, 301, 302, 400, 401, 500, 501, 600 and 601 return INVALID_INPUT, INVALID_REQUESTER_INFO, SERVICE_UNAVAILABLE, MS_UNAVAILABLE, TIMEOUT, VAT_BLOCKED, IP_BLOCKED, GLOBAL_MAX_CONCURRENT_REQ, GLOBAL_MAX_CONCURRENT_REQ_TIME, MS_MAX_CONCURRENT_REQ and MS_MAX_CONCURRENT_REQ_TIME. Point the function at it with check_vies("DE301", url="https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-test-service").
7. VIES directly or through an API
Calling VIES yourself is free. What it leaves to you is GB numbers, a format check before the call, retries and a record of each check. A VAT validation API such as vatcheckapi wraps those into one request:
| VIES directly | vatcheckapi | |
|---|---|---|
| Price | Free | 150 free checks a month, then paid plans |
| Countries | 27 EU member states and XI | The same plus GB, 29 country codes in one call |
| Format and check digit | Not checked first | Checked before the register is asked |
| Name and address | Where the member state shares them | The same; also for GB numbers |
| Register down | An error code in a 200 response | Up to 5 attempts within about 55 seconds, then a 503, which is not billed (at once when VIES reports the country as down) |
| Consultation number | With your requester number | With requester_vat_number (EU and XI numbers) |
curl -G https://api.vatcheckapi.com/v2/check \
-d vat_number=LU26375245 \
-d requester_vat_number=ATU78304602 \
-H "apikey: $VATCHECKAPI_KEY"
The answer has format_valid, checksum_valid and registration_info with is_registered, name, address, consultation_number and checked_at. The Validate VAT Number documentation lists every field; validate VAT numbers in code has examples in JavaScript, Python and PHP. To try a number without code, use the VAT number validator.