To validate a VAT number, you send it to the tax authority that issued it and get back a live yes or no, plus the registered business name and address where that authority publishes them. For the 27 EU member states and Northern Ireland, that authority is reached through VIES. For Great Britain, it's HMRC. Since 1 January 2021 those two systems no longer overlap, so an integration built for one cannot check the other's numbers.
This guide covers the practical ways to handle VAT number validation in 2026: what the authorities provide, the official VIES and HMRC APIs with working code, and a single API that routes to the right authority for you. If you've read How to Validate an IBAN in JavaScript & Python (Beyond the Mod-97 Checksum), the pattern will be familiar. A format check catches typos, and only a live lookup tells you whether the number is real.
For a single number and no code, the VAT Number Checker runs the same live VIES or HMRC lookup in the browser. The rest of this guide is for building the check into a product or process.
TL;DR
- VIES checks EU numbers against the issuing member state's database in real time, and it covers the 27 EU countries plus Northern Ireland (
XI). - UK numbers go to HMRC, because VIES stopped validating
GBnumbers on 1 January 2021 and HMRC's Check a UK VAT Number API now needs registration and OAuth. - Pass your own VAT number as the requester and you get a consultation number, a reference you can show a tax authority as a record of the check.
- Some member states, Germany among them, withhold the name and address and still confirm that the number is valid.
- A single API can route EU numbers to VIES and UK numbers to HMRC from one endpoint, based on the prefix.
What a VAT Number Check Confirms
A successful check tells you three things. The number is currently registered and active in the issuing country's VAT database. In most cases you also get the business name and address attached to it. And if you supplied your own VAT number, you get a consultation number tying the result to a date and time.
A valid result confirms the number exists. It doesn't confirm that the person giving it to you works for that business. The VIES help text says validity is only one element of the evidence supporting a VAT-exempt intra-Community supply, so treat the check as one required step in your due diligence.
Method 1: Validate an EU VAT Number with VIES
VIES (VAT Information Exchange System) is the European Commission's front end to all 27 member states' VAT databases, plus Northern Ireland under the XI prefix. VIES VAT number validation is available through a web form, a SOAP service and a REST endpoint, and none of them requires an account. The Vies on-the-Web - European Commission web form takes a member state and a number without its prefix, with an optional field for your own VAT number. Use it for occasional manual checks, and record the result if you're relying on it for an invoice.
The VIES API (REST Endpoint)
For anything automated, the VIES API takes the country code and number as JSON. It isn't described in the VIES FAQ, which documents the SOAP service, so the behavior below comes from live calls made on 14 October 2026. Here's a minimal Python version:
import requests
VIES_URL = "https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number"
def check_vies(country_code: str, vat_number: str) -> dict:
payload = {"countryCode": country_code, "vatNumber": vat_number}
resp = requests.post(VIES_URL, json=payload, timeout=15)
data = resp.json()
# VIES signals service problems (member state down, too many requests,
# bad input) with actionSucceed: false instead of a validity answer.
if data.get("actionSucceed") is False:
errors = [e.get("error") for e in data.get("errorWrappers", [])]
raise RuntimeError(f"VIES could not answer: {errors}")
return data
result = check_vies("IE", "6388047V")
print(result["valid"], result["name"], result["address"])
This is the real response VIES returned for that call on 14 October 2026, unedited:
{
"countryCode" : "IE",
"vatNumber" : "6388047V",
"requestDate" : "2026-10-14T07:40:27.752Z",
"valid" : true,
"requestIdentifier" : "",
"name" : "GOOGLE IRELAND LIMITED",
"address" : "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4",
"traderName" : "---",
"traderStreet" : "---",
"traderPostalCode" : "---",
"traderCity" : "---",
"traderCompanyType" : "---",
"traderNameMatch" : "NOT_PROCESSED",
"traderStreetMatch" : "NOT_PROCESSED",
"traderPostalCodeMatch" : "NOT_PROCESSED",
"traderCityMatch" : "NOT_PROCESSED",
"traderCompanyTypeMatch" : "NOT_PROCESSED"
}
The trader* fields belong to VIES's optional approximate matching, where you send the name and address you have on file and VIES reports whether each one matches. They stay at --- and NOT_PROCESSED when you don't send them. The requestIdentifier is empty because no requester number was included.
VIES also accepts a GET request, with the country code and number in the URL:
curl "https://ec.europa.eu/taxation_customs/vies/rest-api/ms/IE/vat/6388047V"
It returns the same registration data under different field names. On 16 October 2026 the validity flag came back as isValid instead of valid, next to a userError value (VALID for that number), originalVatNumber and vatNumber. Check which form your code calls before you read the response, because valid and isValid are not interchangeable.
Getting a consultation number from VIES
Add requesterMemberStateCode and requesterNumber (your own VAT registration) to the request body and VIES returns a populated requestIdentifier, which the web form labels the Consultation Number. The VIES help text tells you to keep it if you ever need to prove to a tax administration that you checked a given number at a given time and got a given reply.
The requester has to be a VIES participant. A request with GB as the requester member state on 14 October 2026 was rejected by VIES with the error VOW-ERR-2500 and the message "The requester member state code is invalid." A business with only a GB registration can still validate EU numbers, but it won't get a VIES consultation number for them.
Where VIES gets awkward
- Name and address can be withheld. The VIES FAQ says only certain member states permit displaying the name and address. A live POST for a valid German number on 14 October 2026 returned
valid: truewithnameandaddressboth set to---. Treat that as valid with details withheld. - One number per REST request. The
check-vat-numberendpoint takes a single country code and number per call, so a list means a loop, and a loop means pacing. - Concurrency limits. When too many requests hit VIES or one member state at once, you get errors such as
MS_MAX_CONCURRENT_REQorGLOBAL_MAX_CONCURRENT_REQinstead of an answer. Retry later rather than marking the number invalid. - Member state downtime. National databases go offline for maintenance. VIES lists Ireland's as unavailable on Sunday nights for up to two hours, and a
check-statusendpoint reports which countries are up at any moment. - No UK numbers. A
GBnumber sent to the REST endpoint on 14 October 2026 returned"actionSucceed": falsewith the errorINVALID_INPUT. The VIES disclaimer confirms the GB service ended on 1 January 2021 and sends traders to the UK tax administration.

Method 2: Validate a UK VAT Number with HMRC
Great Britain numbers are checked against HMRC's own register. Numbers with the XI prefix, issued to businesses operating under the Protocol on Ireland and Northern Ireland, go through VIES as covered above.
Check a UK VAT Number on GOV.UK
HMRC publishes a web checker on GOV.UK, Check a UK VAT number. It tells you whether a UK VAT number is valid and shows the name and address it's registered to. A UK VAT-registered business can add its own number to get proof of when it checked. The service can't search by business name, so you need the number itself, which GOV.UK says most businesses print on their invoices, receipts and websites.
HMRC's Check a UK VAT Number API
The programmatic route is version 2 of HMRC's Check a UK VAT Number API, which changed significantly in 2025. Older tutorials that call it with a plain GET request no longer work. Per HMRC's documentation:
- Version 1 was removed on 17 February 2025. Version 2 is the only version, and it sits behind authentication.
- Registration comes first. HMRC says it takes around two weeks, and production credentials are issued after you've tested in the sandbox and accepted the Terms of Use 2.0.
- Authentication is OAuth 2.0 client credentials with the
read:vatscope, plus anAccept: application/vnd.hmrc.2.0+jsonheader. - The standard rate limit is 3 requests per second per application. Going over returns HTTP 429, and HMRC's Reference guide - HMRC Developer Hub - GOV.UK advises against batching requests.
Once you have credentials, a check looks like this:
import requests
BASE = "https://api.service.hmrc.gov.uk"
def get_token(client_id: str, client_secret: str) -> str:
resp = requests.post(f"{BASE}/oauth/token", data={
"grant_type": "client_credentials",
"client_id": client_id,
"client_secret": client_secret,
"scope": "read:vat",
}, timeout=15)
resp.raise_for_status()
return resp.json()["access_token"]
def check_hmrc(token: str, target_vrn: str, requester_vrn: str | None = None) -> dict:
path = f"/organisations/vat/check-vat-number/lookup/{target_vrn}"
if requester_vrn:
path += f"/{requester_vrn}" # returns a consultationNumber
resp = requests.get(BASE + path, headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.hmrc.2.0+json",
}, timeout=15)
resp.raise_for_status()
return resp.json()
The VRN goes in without the GB prefix, as 9 or 12 digits. A number that isn't registered comes back as HTTP 404 with the code NOT_FOUND, and a malformed one as HTTP 400 with INVALID_REQUEST. When you include your own VRN as the requester, the response adds a consultationNumber next to the processingDate. This is the HMRC equivalent of the VIES consultation number.
Method 3: Validate a VAT Number from Either Region with One API
If your customers span the EU and the UK, the two official routes mean separate integrations with separate error formats and rate limits, plus HMRC's registration wait before you can ship. The VAT Number Validation API puts both behind one endpoint, so a single VAT validation call covers either region. You pass the full number with its prefix, and it routes EU numbers to VIES and UK numbers to HMRC automatically.
The request is a single GET with your API key in the X-apiKey header:
curl -X GET 'https://api.apifreaks.com/v1.0/vat/validation?vatNumber=IE6388047V' \
-H 'X-apiKey: YOUR_API_KEY'
In JavaScript:
const API_KEY = process.env.APIFREAKS_API_KEY;
async function validateVat(vatNumber, requesterVat) {
const url = new URL("https://api.apifreaks.com/v1.0/vat/validation");
url.searchParams.set("vatNumber", vatNumber);
if (requesterVat) url.searchParams.set("requesterVatNumber", requesterVat);
const res = await fetch(url, { headers: { "X-apiKey": API_KEY } });
const body = await res.json();
// An unregistered GB number comes back as a 404 with only a message.
if (res.status === 404) {
return { validation: { is_valid: false }, message: body.message };
}
if (!res.ok) {
throw new Error(`${res.status}: ${body.message}`);
}
return body;
}
const result = await validateVat("IE6388047V");
console.log(result.validation.is_valid, result.company?.company_name);
And in Python:
import os
import requests
API_KEY = os.environ["APIFREAKS_API_KEY"]
def validate_vat(vat_number: str, requester_vat: str | None = None) -> dict:
params = {"vatNumber": vat_number}
if requester_vat:
params["requesterVatNumber"] = requester_vat
resp = requests.get(
"https://api.apifreaks.com/v1.0/vat/validation",
params=params,
headers={"X-apiKey": API_KEY},
timeout=20,
)
# An unregistered GB number comes back as a 404 with only a message.
if resp.status_code == 404:
return {"validation": {"is_valid": False}, "message": resp.json().get("message")}
resp.raise_for_status()
return resp.json()
data = validate_vat("IE6388047V")
print(data["validation"]["is_valid"], data["validation"].get("consultation_authority"))
print(data.get("company", {}).get("company_name"))
Here's the response for that same Irish number, captured live on 14 October 2026 and pretty-printed for readability (the API returns it as compact JSON, with the same fields, order and values):
{
"country_code": "IE",
"vat_number": "6388047V",
"requested_at": "2026-10-14T13:21:06.763Z",
"validation": {
"is_valid": true,
"consultation_authority": "VIES"
},
"company": {
"company_name": "GOOGLE IRELAND LIMITED",
"company_address": "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4"
}
}
The call returned HTTP 200.
EU and UK numbers return the same response shape. The fields you'll use most:
| Field | What it holds |
|---|---|
country_code |
The prefix extracted from the number, such as IE or GB |
vat_number |
The number without its prefix |
requested_at |
ISO 8601 timestamp of the check |
validation.is_valid |
true if the number is active and registered |
validation.consultation_authority |
VIES or HMRC, so you know which register answered |
validation.consultation_number |
The official reference, present only when you send requesterVatNumber |
company.company_name |
Registered name, --- when VIES withholds it, or an empty string when unavailable |
company.company_address |
Registered address, --- when VIES withholds it, or an empty string when unavailable |
Passing your own number as requesterVatNumber adds requester_country_code and requester_vat_number to the response, along with the consultation_number, so the reference lands in the object you're already storing.
When details are withheld or the number isn't registered
A German number with details withheld by VIES still comes back as valid. Here's the real response for DE811128135 from the same session, again pretty-printed:
{
"country_code": "DE",
"vat_number": "811128135",
"requested_at": "2026-10-14T13:21:09.681Z",
"validation": {
"is_valid": true,
"consultation_authority": "VIES"
},
"company": {
"company_name": "---",
"company_address": "---"
}
}
An EU number that VIES finds unregistered still returns HTTP 200, with is_valid set to false and empty company fields. This is the real response for IE1234567T:
{
"country_code": "IE",
"vat_number": "1234567T",
"requested_at": "2026-10-14T13:23:13.130Z",
"validation": {
"is_valid": false,
"consultation_authority": "VIES"
},
"company": {
"company_name": "",
"company_address": ""
}
}
A UK number that isn't registered returns a 404 with a short message instead of a validation object. This is the real response for GB000000000, with its status line:
HTTP/2 404
{"message":"targetVrn does not match a registered company"}
Read the HTTP status before validation.is_valid. An unregistered EU number gives you a 200 with is_valid: false, while an unregistered GB number gives you a 404 with no validation object.
Adding a requester for a consultation number
To get an official reference for a UK check, pass your own VAT number as requesterVatNumber. This is the response from 14 October 2026 for a GB target number with a requester, pretty-printed, with the requester VAT number masked. Every other field and value is as returned, and the call returned HTTP 200.
curl -X GET 'https://api.apifreaks.com/v1.0/vat/validation?vatNumber=GB466385806&requesterVatNumber=YOUR_GB_VAT_NUMBER' \
-H 'X-apiKey: YOUR_API_KEY'
{
"country_code": "GB",
"vat_number": "466385806",
"requester_country_code": "GB",
"requester_vat_number": "*********",
"requested_at": "2026-10-14T13:26:16.000Z",
"validation": {
"is_valid": true,
"consultation_number": "PKJ-RPQ-HQL",
"consultation_authority": "HMRC"
},
"company": {
"company_name": "FEED FACTORS LIMITED",
"company_address": "THE GALLERY, WHITTAKER AVENUE, RICHMOND, TW9 1EH, GB"
}
}
The consultation_authority is HMRC here, so you can store the authority next to the reference. The two numbers in this capture belong to unrelated UK businesses and were used for demonstration only. In production, the requester is your own registration.
Parameters and error codes are in the VAT Number Validation API Reference.
Choosing the Right Way to Validate VAT Numbers
| Method | Covers | Setup | Proof of the check | Best for |
|---|---|---|---|---|
| VIES web form | EU + XI | None | Consultation number, with your EU or XI number | Occasional manual EU checks |
| VIES REST API | EU + XI | None (no key needed) | Consultation number, with your EU or XI number | Automated EU-only checks |
| GOV.UK checker | GB | None | Proof of when you checked, with your UK number | Occasional manual UK checks |
| HMRC API v2 | GB | Registration, sandbox testing, OAuth | Consultation number, with your UK number | UK-only systems already integrated with HMRC |
| VAT Number Validation API | EU + GB | API key | Consultation number, via requesterVatNumber |
Products that serve both markets from one integration |
Common Mistakes When You Validate VAT Numbers
Sending the prefix twice produces a misleading result. VIES and HMRC both want the number separated from its country code. When I sent IE as the country code with IE6388047V as the number, a number that is valid on its own, VIES returned valid: false on 14 October 2026, which looks like an unregistered number. Strip spaces, dots and the prefix before building the request.
A service error is not an invalid number. A VIES concurrency error or a member state outage says nothing about the registration. Rejecting a checkout on that basis turns an infrastructure hiccup into a lost sale, so queue the check for a retry and flag the order.
Greece is EL in the VAT system, not its ISO code GR, so a lookup with GR will not work.
A stored result describes the registration on the day you checked it. Registrations can be cancelled, so a result saved months ago may not reflect today's status.
Store the consultation number, the timestamp and the authority that answered next to the order. If a number fails and you end up charging VAT after all, the VAT Rates API returns the rate for the customer's country.
VAT Number Formats by Country
Every VAT number starts with a two-letter prefix followed by a block whose structure depends on the country. A format check before the authority call saves a round trip on obvious typos. The structures below come from the VIES FAQ table, which publishes them because the Commission can't share the check-digit algorithms member states use. Rows were checked against the live VIES application on 14 October 2026. Great Britain isn't in the table because VIES no longer serves it. HMRC's API expects a UK VRN of 9 or 12 digits, sent without the GB prefix.
| Prefix | Country | Structure after the prefix |
|---|---|---|
| AT | Austria | 1 block of 9 characters |
| BE | Belgium | 1 block of 10 digits |
| BG | Bulgaria | 1 block of 9 digits or 1 block of 10 digits |
| CY | Cyprus | 1 block of 9 characters |
| CZ | Czechia | 1 block of either 8, 9 or 10 digits |
| DE | Germany | 1 block of 9 digits |
| DK | Denmark | 4 blocks of 2 digits |
| EE | Estonia | 1 block of 9 digits |
| EL | Greece | 1 block of 9 digits |
| ES | Spain | 1 block of 9 characters |
| FI | Finland | 1 block of 8 digits |
| FR | France | 1 block of 2 characters, 1 block of 9 digits |
| HR | Croatia | 1 block of 11 digits |
| HU | Hungary | 1 block of 8 digits |
| IE | Ireland | 1 block of 8 characters or 1 block of 9 characters |
| IT | Italy | 1 block of 11 digits |
| LT | Lithuania | 1 block of 9 digits, or 1 block of 12 digits |
| LU | Luxembourg | 1 block of 8 digits |
| LV | Latvia | 1 block of 11 digits |
| MT | Malta | 1 block of 8 digits |
| NL | Netherlands | 1 block of 12 characters |
| PL | Poland | 1 block of 10 digits |
| PT | Portugal | 1 block of 9 digits |
| RO | Romania | 1 block of minimum 2 digits and maximum 10 digits |
| SE | Sweden | 1 block of 12 digits |
| SI | Slovenia | 1 block of 8 digits |
| SK | Slovakia | 1 block of 10 digits |
| XI | Northern Ireland | 1 block of 3 digits, 1 block of 4 digits and 1 block of 2 digits; or the above followed by a block of 3 digits; or 1 block of 5 characters |
Austrian numbers are usually written ATU12345678. The VIES FAQ says the correct prefix is AT and the first position of the number is always U, so enter the U as part of the number.
Since 1 January 2025, small businesses that use the EU's cross-border SME exemption scheme receive an identification number ending in EX, assigned by the member state where they're established. Ireland's regulations, S.I. No. 69/2025 - European Union (Value-Added Tax) Regulations 2025, define it as the number ending with the suffix EX. The VIES FAQ doesn't say how these numbers are handled, so test with a real EX number before you build rules around them. A right-length number can still be unregistered or made up, so the authority lookup is the step that settles validity.
Conclusion
The way to validate a VAT number depends on where your customers are. EU-only businesses can get far with the VIES REST endpoint, provided they handle withheld names, single-number requests and member state downtime. UK-only businesses need HMRC's authenticated API and its registration wait. If you sell into both, one endpoint keeps the routing, error handling and audit trail together, so you can validate VAT numbers from both regions without maintaining two integrations. Whichever route you pick, keep the consultation number with the order.
Frequently Asked Questions
How do I validate a VAT number?
Send the number, with its country prefix, to the authority that issued it. For EU numbers and Northern Ireland numbers that authority is reached through VIES, which has a web form and a REST endpoint that need no account. For Great Britain numbers it's HMRC, through the GOV.UK checker or HMRC's authenticated API. To handle both regions from one integration, a single API such as the VAT Number Validation API routes each number by its prefix.
What does a valid VAT number look like?
A VAT number is a two-letter country prefix followed by a national block whose length and characters depend on the country. Germany's is DE plus 9 digits, and Ireland's is IE plus 8 or 9 characters. Greece uses the prefix EL and Northern Ireland uses XI. A UK VRN sent to HMRC is 9 or 12 digits, without the GB prefix. The format table in this guide lists every EU prefix. A correct format doesn't mean the number is registered, so you still need the live check.
Can I check if a VAT number is valid online?
Yes. For EU and Northern Ireland numbers, the European Commission's VIES page returns a valid or invalid result from the issuing member state's database. For UK numbers, GOV.UK's Check a UK VAT number page shows the registered business name and address for a valid number. In both cases you need the number itself, because neither can search by business name.
How do I look up a VAT number from a company name?
You can't through the official checkers. The VIES FAQ says the Commission has no access to that information and that the VAT number should come from your customer or supplier. GOV.UK's UK checker likewise cannot search by business name, and says most businesses print their number on invoices, receipts and websites.
Are VAT numbers public information?
Anyone with the number can check it. GOV.UK's checker returns the registered business name and address for a valid UK number, and VIES returns the name and address for member states that allow it. The VIES FAQ notes that some member states don't, so a valid result can come back with those fields blank.
What should I do if a VAT number comes back invalid?
First confirm with your customer that the number is correct, including its length and country prefix. If it still returns invalid, the VIES FAQ says to ask the business to contact its own tax administration, because only national tax administrations can update VIES data.
Start validating VAT numbers with 10,000 free credits. The VAT Number Validation API requires no credit card to get started. Create a free account.
