Skip to Content
GuidesValidate VAT

Validate VAT

VerifNow validates EU VAT numbers in two independent steps: it checks the number’s structure against the rules of its member state, then checks its registration against VIES, the European Commission’s VAT Information Exchange System.

Those two answers come from different places, and only one of them is always available. This guide is mostly about that difference, because it is where VAT validation actually goes wrong in production.


Basic VAT validation

Send a POST request to /api/v1/validate/vat with the number, country prefix included:

POST https://api.verifnow.io/api/v1/validate/vat X-API-KEY: your_api_key_here Content-Type: application/json { "value": "IE6388047V" }

Example response

{ "valid": true, "message": "Valid VAT number", "normalizedValue": "IE6388047V", "originalValue": "IE6388047V", "validationLevel": "STANDARD", "vatDetails": { "format_valid": true, "registered": true, "country_code": "IE", "source": "LIVE", "checked_at": "2026-09-08T02:21:25Z", "trader_name": "GOOGLE IRELAND LIMITED", "trader_address": "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4", "vies_available": true } }

trader_name and trader_address are only present when the member state chooses to disclose them. Germany, for instance, does not.


The problem this endpoint exists to solve

VIES is the only authoritative source for EU VAT registration, and it is unreliable by design: it is a federation of 27 national registries, each of which can go down on its own. The Commission publishes no SLA, and individual member states drop out several times a month.

Most VAT validation APIs handle this in one of two ways, and both are wrong:

  • They return an error, so your checkout fails for a customer whose VAT number is perfectly valid.
  • They return valid: false, which is worse — you tell a legitimate business that their VAT number is not registered, when the truth is that you could not ask.

registered is nullable, and null never means “no”. A VAT number whose registration could not be checked is not an unregistered VAT number. If you collapse the two, you will reject real customers during someone else’s outage.


The source field

Every VAT response carries a source telling you where the registration verdict came from. Branch on this, not on valid, whenever the distinction matters for your own compliance.

sourceWhat it meansregistered
LIVEConfirmed against VIES during this request.true / false
CACHEServed from a VIES answer less than 24 hours old.true / false
STALEVIES was unreachable, so an older cached answer was used.true / false
UNVERIFIEDVIES was unreachable and nothing was cached.null
NOT_APPLICABLEThe country is outside VIES; no registry lookup exists.null

How the fallback works

  1. Structure is checked locally, always, and never depends on VIES.
  2. A fresh cached answer (under 24 hours) is served directly.
  3. Otherwise VIES is called live, with a 4-second budget.
  4. If VIES is unreachable, a stale cached answer is used — up to 30 days old.
  5. If nothing is cached either, the response is UNVERIFIED.

Stale answers are served on purpose. VAT registrations change on the order of months, so a three-week-old confirmation is far better evidence than refusing a customer outright.


Handling degraded responses

The pattern that survives a VIES outage:

import { VerifNow } from '@verifnow/sdk'; const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY }); const result = await client.validateVat('IE6388047V'); const { formatValid, registered, source } = result.vatDetails; if (!formatValid) { // Structurally impossible. Safe to reject — this never depended on VIES. return reject('That VAT number is not correctly formed.'); } if (registered === false) { return reject('That VAT number is not registered.'); } if (registered === null) { // source is UNVERIFIED. Do not reject: accept and re-check later. await queueForRecheck(vatNumber); return accept({ verified: false }); } return accept({ verified: true, stale: source === 'STALE' });

The SDK maps the API’s snake_case fields onto camelCase, so format_valid in the JSON above reads as formatValid here. registered keeps its three states — the SDK maps it with its own helper precisely so null is not flattened into undefined. Needs @verifnow/sdk 1.1.0 or later.

For reverse-charge invoicing you generally need evidence that you checked, not a guarantee that the registry was up. Store the source and checked_at you received alongside the invoice — and the consultation_number, once your account has a VAT number (below).


Checking VIES availability yourself

VerifNow publishes per-country VIES availability:

GET https://api.verifnow.io/api/v1/status/vies

This endpoint needs no API key. The human-readable version lives at verifnow.io/en/status .

Two things are combined there. The Commission publishes a per-country status feed about itself, but only as a current snapshot — no history, so you cannot tell a country that has been down for an hour from one that has been flaky all month. We sample that feed every five minutes and keep the history. On top of it, a country we have actually failed to reach three times in a row is reported as unavailable even if the Commission’s feed still claims otherwise; each country in the response says which of the two sources decided its verdict.

Use it to decide whether to run a bulk re-check now or wait, or to explain to your own users why a verification is pending.


Supported countries

All 27 member states, plus:

  • EL — Greece uses EL, not GR, in VAT numbers.
  • XI — Northern Ireland, which remains inside the EU VAT area for goods.

Each member state has its own structural rule, and several allow letters inside the number: IE6388047V, NL123456789B01, ESX1234567X, ATU12345678. A validator that assumes the body is all digits will produce false negatives on legitimate registrations.


Common errors

SituationResponse
Missing country prefix (6388047V)valid: false, format_valid: false
Country outside the EUsource: NOT_APPLICABLE, registered: null
Correct structure, absent from the registryvalid: false, registered: false
VIES down for that countrysource: UNVERIFIED, registered: null

See Error Handling for transport-level failures and status codes.


Checking the holder’s name

Send the company you expect with the number, and vatDetails says whether it is the registered holder:

POST https://api.verifnow.io/api/v1/validate/vat Content-Type: application/json { "value": "IE6388047V", "traderName": "Google Ireland Ltd" }
FieldValues
trader_name_matchMATCH, MISMATCH, or NOT_AVAILABLE when no comparison was possible
trader_name_match_sourceVERIFNOW — compared with the name VIES publishes, ignoring case, accents, punctuation and common legal forms; VIES — VIES compared it itself

Who can compare depends on the member state. Measured on 24 September 2026 across 15 of them: 13 publish the holder’s name, so VerifNow compares; Spain publishes none but has VIES check one; Germany does neither, so the answer is NOT_AVAILABLE. An unregistered or unconfirmed number is also NOT_AVAILABLE — there is no holder to compare with. See Before you pay a new supplier for the measurement and how to act on a mismatch.

traderName is optional, at most 200 characters, and does not cost an extra validation. From the SDKs: client.validateVat(number, { traderName }) in @verifnow/sdk 1.9.0, and client.validateVat(number, name) in io.verifnow:verifnow-spring 2.9.0.


Keeping proof of the check

VIES issues a consultation number — its receipt for a given number, checked at a given time, with a given reply — only when the request names a requester. In VerifNow the requester is the VAT number on your account: add it in the dashboard under Profile, and every lookup made with your API keys comes back with one.

"vatDetails": { "registered": true, "source": "LIVE", "checked_at": "2026-09-27T14:03:11Z", "consultation_number": "WAPIAAAAX8k1abcd" }
  • The receipt names you, so it is never shared. A lookup that another customer’s receipt could answer is made live for you instead.
  • Within 24 hours, your receipt is reused. VIES treats validating the same number several times a day as abuse, so a second lookup returns "source": "CACHE" with the same consultation_number and checked_at as the first.
  • When VIES is down, you get your own most recent receipt for that number, with "source": "STALE" and its original date — evidence of an earlier check, not of one made today.
  • No VAT number on the account, no receipt. The verdict is the same; consultation_number is absent. VIES must know your number: the profile refuses one VIES reports as unregistered, and if VIES later rejects it, lookups carry on without a receipt rather than failing.

Every identified lookup is also kept in the dashboard under VAT audit — number, date, reply, consultation number, and the name and address VIES returned — searchable and exportable as CSV: 30 days on Free, one year on Starter, three on Growth, ten on Pro. Keep your own copy with the invoice all the same: the period is set by each member state, and can run on while an assessment is open.

What the receipt proves, and what it does not, is the subject of What a VIES consultation number proves.


Next steps

  • VAT Rates — the standard, reduced and regional rates of the 27 member states, no key needed
  • Validate IBAN — the other field a billing form depends on
  • Node.js example — full integration with @verifnow/sdk
  • Rate Limits — quotas and concurrency
Last updated on