How often is VIES actually down?
Everyone building EU VAT validation eventually learns that VIES is unreliable. What nobody publishes is how unreliable, per country, with numbers you can check.
Figures do circulate. We have seen “32% monthly downtime” quoted for Germany and Belgium in competitor marketing, with no methodology attached. So we started measuring instead, and this page is the result.
72,632 measurements. 28 member states, sampled every five minutes over nine days. The live
version of this data is at verifnow.io/en/status , and the raw
feed is public at GET /api/v1/status/vies — no API key.
The numbers
| Member state | Available | Roughly, over nine days |
|---|---|---|
| 🇱🇻 Latvia | 76.33% | ~51 hours unreachable |
| 🇩🇪 Germany | 84.12% | ~34 hours |
| 🇧🇪 Belgium | 85.89% | ~31 hours |
| 🇨🇾 Cyprus | 89.86% | ~22 hours |
🇬🇷 Greece (EL) | 94.80% | ~11 hours |
| 🇸🇮 Slovenia | 98.03% | ~4.3 hours |
Northern Ireland (XI) | 99.19% | |
| 🇳🇱 Netherlands | 99.27% | |
| 🇨🇿 Czechia · 🇩🇰 Denmark · 🇮🇪 Ireland | 99.31% | |
| 🇧🇬 Bulgaria · 🇲🇹 Malta | 99.42% | |
| 🇸🇪 Sweden | 99.54% | |
| 🇫🇷 France · 🇮🇹 Italy · 🇵🇱 Poland | 99.65% | |
| 🇦🇹 Austria · 🇪🇪 Estonia · 🇪🇸 Spain · 🇱🇹 Lithuania · 🇱🇺 Luxembourg · 🇷🇴 Romania · 🇸🇰 Slovakia | 99.77% | |
| 🇭🇷 Croatia · 🇭🇺 Hungary · 🇵🇹 Portugal | 99.88% | |
| 🇫🇮 Finland | 100.00% | none observed |
The median member state sat at 99.60%. That is the part most people get wrong: VIES is not uniformly broken. Twenty-two of twenty-eight registries were above 99%.
The problem is the tail. Five countries fell below 95%, and Latvia spent nearly a quarter of the period unreachable. If your customers are Latvian, German or Belgian, “VIES is usually fine” is not a useful statement.
What we actually measured
Being precise about this matters, because the number means different things depending on how it was produced.
We did not probe each registry ourselves. The European Commission publishes its own per-country availability feed. We read that feed every five minutes and keep the history — which the Commission does not, since its feed reports only the current moment.
We do correct it downward. When our own lookups to a country fail three times in a row, we record that country as unavailable for as long as our circuit breaker stays open, regardless of what the feed claims. A registry that reports itself healthy while refusing requests is counted as down.
So the figures are the Commission’s own self-assessment, made durable and made stricter. They are not an independent probe, and we would rather say so than imply a rigour the method does not have.
It also means these figures are not directly comparable to the 32% quoted at the top of this page. We don’t know how that number was produced, and our method reads the Commission’s own feed, which may understate the request failures a caller actually experiences. The point is not that one number is right and the other wrong — it is that only one of them tells you how it was measured.
Nine days is still a short window, and these numbers will move. Treat the ranking as informative and the precise decimals as provisional. The status page always shows the current thirty-day window.
Why this breaks checkouts
A VAT lookup that fails is not the problem. The problem is what most code does next.
VIES answers HTTP 200 during an outage, carrying the real outcome in a userError field. Code that only checks the status code reads an outage as a successful not registered answer — and rejects a legitimate customer at checkout.
That failure is not evenly distributed either. Two of the codes VIES returns are rate limits, which means the failure arrives precisely when your traffic is highest.
// What an outage looks like. Note the status code.
HTTP/1.1 200 OK
{ "isValid": false, "userError": "MS_UNAVAILABLE" }Reading isValid here tells your customer their VAT number is not registered. It is.
What to do instead
Treat “registered” as three states, not two. A lookup can say yes, say no, or fail to say anything — and the third is not the second.
import { VerifNow } from '@verifnow/sdk'
const client = new VerifNow({ apiKey: process.env.VERIFNOW_API_KEY! })
const { vatDetails } = await client.validateVat(vatNumber)
if (!vatDetails!.formatValid) {
// Structural, decided locally, never depends on VIES. Safe to reject.
return reject('That VAT number is not correctly formed.')
}
if (vatDetails!.registered === false) {
return reject('That VAT number is not registered.')
}
if (vatDetails!.registered === null) {
// Latvia was down. Accept, flag it, re-check later — do not reject.
await queueForRecheck(vatNumber)
return accept({ verified: false })
}
return accept({ verified: true, stale: vatDetails!.source === 'STALE' })registered is null — never false — when the registry could not be reached, and source tells you where the verdict came from:
source | Meaning |
|---|---|
LIVE | Confirmed against VIES during this request |
CACHE | A VIES answer less than 24 hours old |
STALE | VIES was unreachable, so an older answer was used |
UNVERIFIED | VIES was unreachable and nothing was cached |
During Latvia’s 51 hours, a caller gets STALE with a real verdict if we have seen that number before, and UNVERIFIED if we have not. Neither is a rejection.
You can implement all of this against VIES directly — the contract matters more than the vendor. If you would rather not, VerifNow does it behind one endpoint, and the free tier covers 250 validations a month.
Check it yourself
The feed behind this page is public and takes no API key:
curl https://api.verifnow.io/api/v1/status/vies{
"generatedAt": "2026-09-14T11:35:42Z",
"windowDays": 30,
"countriesTracked": 28,
"countriesUnavailableNow": 0,
"countries": [
{ "countryCode": "LV", "availabilityPercent": 76.33, "sampleCount": 2594 }
]
}If you are building anything that depends on VIES, monitoring it is cheap and the alternative is finding out from support tickets.
Related
- Validate VAT — the full degradation contract
- Validate IBAN — the other field a billing form depends on
- Node.js examples —
@verifnow/sdk