temsor API
← Guides

EU VAT number validation: a VIES timeout is not “invalid”

VIES is free and drops member states one at a time. Treating a timeout as invalid charges VAT to a B2B buyer who should have been reverse-charged.

Updated 2026-08-21

Intra-EU B2B invoices hang on one question: is this counterparty’s VAT number registered in the member state that issued it? If yes, you reverse-charge and charge 0% VAT. If no, you add your domestic rate. Get that binary wrong in either direction and you either under-collect tax or overcharge a customer who then disputes the invoice. The official register is VIES. VIES is free. VIES is also the unreliable part of the job.

What actually fails in production

VIES is not one service. Each member state runs its own lookup; the Commission’s REST façade (ec.europa.eu/taxation_customs/vies/rest-api) fans out. Germany being up says nothing about Italy. A 503, a 12-second hang, or an empty body is a transport failure. A false from a healthy member-state service is a registration failure. Those two must not share a status code in your billing engine, because they have opposite commercial meanings:

The trap: most wrappers, including a lot of “VAT API” landing pages, map any non-200 onto valid: false. That is the expensive bug. The number’s format can be perfect and the company real; you just asked Italy during a window where Italy’s VIES node was down.

Check format locally before you spend an upstream call

A German VAT number is DE + 9 digits. Ireland has three historic shapes. Romania is 2–10 digits. Prefixing GB is not a typo you can “fix” into VIES: Great Britain left; Northern Ireland businesses use XI. Switzerland, Norway and Türkiye are not in the EU VAT area at all — looking them up on VIES is a category error, not a failed checksum.

function parseEuVat(raw) {
  const s = String(raw).replace(/[\s.\-_/]/g, '').toUpperCase();
  const m = /^([A-Z]{2})(.+)$/.exec(s);
  if (!m) return { ok: false, reason: 'missing country prefix' };
  const [, code, number] = m;
  // DE: 9 digits. Do not call VIES if this fails.
  if (code === 'DE' && !/^\d{9}$/.test(number)) {
    return { ok: false, reason: 'German VAT numbers are DE + 9 digits' };
  }
  return { ok: true, code, number };
}

Format failure is the one case you can call invalid without talking to Brussels. Everything past this point needs the register — and a third state for when the register is silent.

The catalogue call

The endpoint talks to VIES, caches the answer for 24 hours, and reports whether that member state’s node currently claims to be up. It never returns invalid because the HTTP call failed.

curl -s https://api.temsor.com/v1/eu/vat/validate \
  -H 'content-type: application/json' \
  -d '{"vatNumber":"DE811907980"}'
{
  "verdict": "valid",
  "formatValid": true,
  "countryCode": "DE",
  "countryName": "Germany",
  "formatted": "DE811907980",
  "registeredName": null,
  "memberStateService": "Available",
  "reasons": []
}

DE811907980 is the Commission’s own documented example number. Most member states do not publish the legal name over VIES; registeredName being null is not a failed lookup, it is the member state declining to share. When VIES is down the same shape comes back with verdict: "unknown" and a reason that names the transport error — your invoicing code can branch on the enum instead of guessing from HTTP status.

import Client from 'temsor-api';
const api = new Client();
const r = await api.euVatValidate({ vatNumber: 'DE811907980' });
if (r.verdict === 'unknown') {
  // queue and retry — do not add VAT yet
} else if (r.verdict === 'invalid') {
  // charge domestic VAT
} else {
  // reverse charge
}

What this does not answer

The remaining work in a real billing engine is “given this registered number, which rate and which OSS rule apply on this invoice date”. That is a table with an asOf, not a live register:

curl -s https://api.temsor.com/v1/eu/vat/rates \
  -H 'content-type: application/json' \
  -d '{"country":"DE","asOf":"2026-01-15"}'

You get the standard / reduced / parking schedule and the Union OSS threshold (EUR 10,000 from 1 July 2021). You do not get which goods sit in which reduced band — that is TEDB, not this endpoint.

Frequently asked

VIES returned an error. Is the VAT number invalid?

No. An upstream timeout, 5xx or a member-state node marked unavailable means the register did not answer. That is unknown, not invalid. Charging VAT in that window is how you fabricate a reverse-charge dispute.

Does a valid VIES result mean I must reverse-charge?

It means the number is registered, which is the usual precondition for intra-EU reverse charge on a B2B supply. Place of supply, the kind of goods or services, and whether you are in OSS still sit with you — the endpoint does not file the return.

Why is Great Britain (GB) rejected?

GB left VIES after Brexit. Northern Ireland businesses that remain in the VAT area use the XI prefix. A GB number is not a malformed EU number; it is outside the system.

Do I need an API key to try this?

No. Anonymous calls run on a per-IP demo quota. A free key raises it. Results are cached for 24 hours so a retry during a VIES blip does not stampede the Commission’s façade.

Endpoints used here

/v1/eu/vat/validate

Open the reference, run it in the browser, no key required.

/v1/eu/vat/rates

Open the reference, run it in the browser, no key required.