VIES is not a database
The mental model most integrations start with is wrong. VIES, the VAT Information Exchange System, stores no VAT numbers at all. It is a relay run by the European Commission: your query goes to Brussels, Brussels forwards it to the national tax database of the member state in the number’s prefix, and the answer travels back the same way.
That architecture explains every operational quirk. There are 27 national systems behind one facade, each with its own hardware, its own load and its own maintenance windows, several of them at night with a regularity you could set a watch by. When one is offline, validations for that country fail while the other 26 keep answering. A “VIES outage” is usually one country’s database taking a nap.
It also means nobody can mirror VIES, which is why every “VIES cache API” you find is either doing live relays like everyone else or serving stale answers.
The error codes, decoded
The service reports trouble with a short list of fault codes. They are worth telling apart, because the correct reaction differs for each.
MS_UNAVAILABLE The member state’s national database is not answering, typically maintenance or an outage on their side. Only that country is affected. Queue the check and retry later, the number itself has not been judged.
MS_MAX_CONCURRENT_REQ Too many simultaneous requests for that member state, across all VIES users combined. Back off and retry with spacing. If your own batch job triggered it, serialize the requests.
GLOBAL_MAX_CONCURRENT_REQ The whole service is at its concurrency ceiling. Same treatment as the per-state variant, with a longer pause.
TIMEOUT The national database took too long to answer. Functionally the same as MS_UNAVAILABLE for you: retry later, and do not interpret it as invalid.
SERVICE_UNAVAILABLE An error on the VIES relay itself rather than a national database. Rare, and usually short.
INVALID_INPUT The request was malformed, most often a country prefix left inside the number field or stray whitespace. This one is your bug, retrying will not change it. Validate the format before sending.
The pattern behind the list: only INVALID_INPUT says something about your request, and none of these codes says anything about the VAT number. Treating any of them as “number invalid” is the single most common integration mistake, and it rejects legitimate customers during every maintenance window.
What your checkout should do while it is down
Split the validation into what you can check offline and what only VIES can answer. Country prefix, length, character pattern and check digits are pure math and never go down. Our VAT ID validator runs exactly these checks in the browser, per country, and catches the typos, which are most of the real-world failures. What VIES adds on top is one fact you cannot compute: whether the number is currently registered for intra-EU trade.
So the flow that holds up is: validate structure immediately, always. If VIES answers, act on the answer. If VIES is unavailable, accept the structurally valid number, mark the record as pending confirmation, and queue the live check. Rejecting the order because a tax authority in another country reboots its database at 2 a.m. is a business decision nobody actually intends to make, it just falls out of treating every error as invalid.
The one decision to make consciously is what happens to the invoice while confirmation is pending, and that belongs to your accountant, not your error handler. The general shape everyone lands on is documented attempt, provisional reverse charge, prompt re-check.
The consultation number, your evidence
Zero-rating a cross-border B2B invoice rests on the customer’s VAT number being valid, and in an audit years later “it said valid back then” is worth nothing without a record. VIES has a built-in answer that many integrations ignore: run the check as a qualified query, supplying your own VAT number as the requester, and the response includes a consultation number.
That consultation number, together with the timestamp and the result, is the receipt. Store all three on the customer record, and store failed attempts too, with their error code. A log line saying you tried during an outage and re-validated successfully four hours later with consultation number X is a defensible story. An empty log is not.
This costs one extra field in the request and one column in your database, which makes it the cheapest compliance feature you will add this year.
A retry strategy that does not make it worse
Because the bottleneck is a national database shared by everyone in Europe, aggressive retries are both useless and antisocial, and the concurrency faults exist precisely because of them.
- Retry
MS_UNAVAILABLEandTIMEOUTon a schedule of once every 30 to 60 minutes, not in a tight loop. Outages last minutes to hours, and the first retry after half an hour resolves most of them. - Treat the concurrency faults as a signal to slow the whole pipeline rather than the one request.
- Give up into a review queue after a day rather than retrying forever, at that point something structural is wrong with the number or the request.
- Log every attempt with its fault code. The log is simultaneously your debugging tool and your audit trail, the same principle as designing error responses a client can act on: distinguishable failures get distinguishable handling.
Re-validating the customer master data
Numbers get deregistered when companies close, merge or lose their intra-EU status, so a check from onboarding ages badly. Recurring billing against a number that went invalid two years ago is exactly the situation the consultation number exists for.
A monthly batch job over active B2B customers covers it. Run it at a quiet hour, serialize the requests, expect a few MS_UNAVAILABLE along the way, and route numbers that come back invalid to a human instead of auto-suspending anyone, because the false-invalid cases from the section above apply to batch jobs too.
VIES in production
What is a VIES consultation number?
A reference the service returns when you run a qualified check, meaning you supplied your own VAT number as the requester alongside the number you are checking. It identifies that exact validation at that exact time, and it is the evidence tax authorities accept that you checked a customer’s number before invoicing without VAT. Plain checks without a requester number return no consultation number, so B2B billing systems should always send one.
Can I issue a reverse-charge invoice while VIES is down?
Common practice is yes, if the syntax and checksum are valid and you log the failed attempt, then re-validate as soon as the service answers and keep the consultation number. Whether that holds in an audit is a question for your tax advisor, not for a status page.
How long is VIES usually unavailable?
Member state outages typically last minutes to a few hours. Each country’s national database has its own maintenance windows, and the Commission publishes the planned ones per member state.
Why does a valid VAT number show as invalid in VIES?
Most often because it is not registered for intra-EU transactions. A number can be perfectly valid domestically and still return invalid in VIES, Spain is the classic case, where a company must be in the ROI register first. Fresh registrations also take days to reach the national database that VIES queries.
How often can I query the VIES API?
There is no published quota, but concurrency limits exist: exceed them and you get MS_MAX_CONCURRENT_REQ. Serialize requests and space out batch jobs.
Should I cache VIES validation results?
Cache the result with its timestamp and consultation number, yes, but treat it as a snapshot rather than a fact. A VAT number can be deregistered any day, which is why recurring billing setups re-validate on a schedule instead of trusting a check from two years ago.
Does VIES have an official status page?
No live one. Planned maintenance windows are published per member state on the VIES site, and unplanned outages just surface as MS_UNAVAILABLE.