EU VAT Number Validator & KYB Checker avatar

EU VAT Number Validator & KYB Checker

Pricing

from $10.00 / 1,000 vat number checks

Go to Apify Store
EU VAT Number Validator & KYB Checker

EU VAT Number Validator & KYB Checker

Bulk-validate EU VAT numbers against the official VIES service. Offline checksum pre-filtering, member-state outage detection, consultation numbers for audit evidence, and company-name matching.

Pricing

from $10.00 / 1,000 vat number checks

Rating

0.0

(0)

Developer

J Act2Labs

J Act2Labs

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

2 days ago

Last modified

Share

Bulk EU VAT number validation against the official VIES service, built for finance and compliance teams who need answers they can defend in an audit. The offline checksum layer is differential-tested against python-stdnum across 568,173 generated numbers, with zero false rejections and zero false acceptances — so a number this Actor rejects without spending an API call really is impossible. Paste a list of VAT numbers or upload customer records, and get back validity, the registered company name and address, a name-match score, and the VIES consultation number that proves you performed the check. Covers all 27 EU member states plus Northern Ireland, with offline checksum validation for Great Britain.

What this VAT validation API does differently

Most VAT checkers make the same mistake: when a national tax database is offline, they report the VAT number as invalid. That is not a cosmetic bug. Acting on it means charging VAT to a legitimate B2B customer, or zero-rating a sale you should have taxed. Both are expensive to unwind.

This Actor never conflates the two. Every result is one of five explicit states:

StatusMeaning
validRegistered and active in VIES
invalidVIES confirms the number is not registered
unverifiableNo authoritative answer — member state offline, timeout, or throttled. Not the same as invalid, and never charged.
malformedFailed its country's official checksum. Rejected locally, no API call spent.
not_supportedOutside VIES coverage (Great Britain since Brexit)

The valid field is true, false, or null. It is only ever null when we genuinely do not know.

Features

  • All 27 EU member states + XI (Northern Ireland), with GR accepted as an alias for EL
  • 🔍 Offline checksum pre-filtering for 29 jurisdictions — typos are caught before an API call is spent, so you are not billed for them
  • 🛡️ Member-state outage detection via the VIES status endpoint, so an outage is never reported as an invalid number
  • 📜 Consultation numbers (requestIdentifier) captured as audit evidence under Article 31 of Regulation 904/2010 — this cannot be obtained retroactively
  • 🏢 Company-name matching for KYB, tolerant of legal-form differences (GmbH vs Gesellschaft mit beschränkter Haftung), diacritics, punctuation, word order and typos
  • 🔁 Correct throttling behaviour — VIES limits by concurrency, not requests per second, so calls to a single member state are serialised and transient errors are retried with exponential backoff
  • 🧠 Automatic de-duplication — the same VAT number twice in one run costs one API call and one charge
  • 🚦 Fails safe on IP_BLOCKED — aborts rather than hammering VIES and getting your address blocked for longer

What data you get back

FieldDescription
normalisedVatIdCountry code + number, uppercase, no separators. Safe as a database key.
status / validSee the table above
formatCheckpass, fail, or unknown (a possible national personal-ID variant, sent to VIES rather than rejected)
companyName / companyAddressAs held by the member state. null where the state withholds it — Germany and Spain always do.
nameMatch / nameMatchScorematch / partial / mismatch, plus a 0–1 score
consultationNumberVIES proof-of-check identifier
checkedAtVIES requestDate where available
referenceYour own row identifier, passed through unchanged
messagePlain-language explanation whenever the result needs one

How to validate EU VAT numbers with this Actor

  1. Simple list — paste VAT numbers into the VAT numbers field, one per line. Prefixes and spacing are optional: DE136695976, FR 40 303.265-045 and ie6388047v are all accepted.
  2. KYB mode — use the Records field instead, with objects containing vatNumber, expectedName and reference, to also verify the registered name matches your records.
  3. Audit mode — fill in Your own VAT number to receive a consultation number with every result.
  4. Run it, then take the results from the dataset as JSON, CSV or Excel, or via the API.

Pricing

Pay per event. You are charged for:

  • A verified answer — VIES confirmed the number as valid or invalid
  • An offline rejection — a number that failed its country's official checksum, billed at a fraction of the verified rate because no API call was made

You are not charged for rows we could not verify (member-state outage, timeout, throttling), or for input outside VIES coverage. Duplicate numbers within a run are charged once.

Input example

{
"records": [
{ "vatNumber": "IE6388047V", "expectedName": "Google Ireland Limited", "reference": "CUST-1001" },
{ "vatNumber": "LU26375245", "expectedName": "Amazon Europe Core", "reference": "CUST-1002" }
],
"requesterVatNumber": "DE123456789",
"maxConcurrency": 4
}

Output example

{
"input": "IE6388047V",
"reference": "CUST-1001",
"countryCode": "IE",
"vatNumber": "6388047V",
"normalisedVatId": "IE6388047V",
"status": "valid",
"valid": true,
"formatCheck": "pass",
"companyName": "GOOGLE IRELAND LIMITED",
"companyAddress": "3RD FLOOR, GORDON HOUSE, BARROW STREET, DUBLIN 4",
"expectedName": "Google Ireland Limited",
"nameMatch": "match",
"nameMatchScore": 1,
"consultationNumber": "WAPIAAAAWuJShAAA",
"checkedAt": "2026-08-12T11:33:23.919Z",
"source": "vies",
"message": null
}

A German number, by contrast, comes back valid with no name, because Germany withholds it:

{
"normalisedVatId": "DE123456789",
"status": "valid",
"valid": true,
"companyName": null,
"companyAddress": null,
"nameMatch": null,
"message": "DE does not disclose the registered name and address through VIES for data-protection reasons."
}

VIES error codes, and why none of them means "invalid"

Every error below arrives from VIES with HTTP status 200 — not the 400 that the official documentation and three independent SDKs all describe. The body looks like this:

{ "actionSucceed": false, "errorWrappers": [{ "error": "MS_MAX_CONCURRENT_REQ" }] }

Note what is missing: there is no valid field. A client written the obvious way — if (response.ok) { /* treat as a successful lookup */ } — accepts every one of these as success, finds no valid field, and degrades the row to "not registered". That is the bug that makes VAT validators report live VAT numbers as invalid whenever VIES is busy.

This Actor reads the error code before it trusts any validity flag. A row is reported valid or invalid only when VIES returned no error code at all.

The official VIES test service documents 13 trigger values. Two of them (100 and 200) are the valid and not-registered happy paths; the other 11 are error codes, and every one of them was captured from the live service and replayed through the real client in this Actor's test suite:

TriggerVIES errorWhat it actually meansWhat this Actor does
300SERVICE_UNAVAILABLEVIES itself is unavailableRetry with exponential backoff, then unverifiable
301MS_UNAVAILABLEThe member state's own database is not respondingRetry, then unverifiable
302TIMEOUTThe member state did not answer in timeRetry, then unverifiable
600MS_MAX_CONCURRENT_REQToo many requests open against one member stateRetry, then unverifiable
601MS_MAX_CONCURRENT_REQ_TIMEThe same limit, expressed over a time windowRetry, then unverifiable
500GLOBAL_MAX_CONCURRENT_REQA VIES-wide concurrency limit, not yours specificallyRetry, then unverifiable
501GLOBAL_MAX_CONCURRENT_REQ_TIMEThe same global limit, over a time windowRetry, then unverifiable
201INVALID_INPUTVIES rejected the country code or number shapeunverifiable, with the reason on the row
202INVALID_REQUESTER_INFOThe requester VAT number you supplied is not usableunverifiable, and the log tells you how to fix it
400VAT_BLOCKEDVIES has blocked lookups for that one numberunverifiable, with the reason on the row
401IP_BLOCKEDVIES has blocked your IP addressAborts the whole run rather than making it worse

Not one of them produces invalid. That behaviour is pinned by the test suite rather than assumed, so it cannot regress silently.

What MS_MAX_CONCURRENT_REQ and GLOBAL_MAX_CONCURRENT_REQ really mean

These are the two you will meet most often, and neither means you sent too many requests per second. VIES limits by concurrency, not by rate. Sequential requests a second apart still hit MS_MAX_CONCURRENT_REQ when the member state is already busy with other people's traffic — France does it routinely, from different IP addresses on different days. Raising your delay between requests will not help; reducing how many you have open at once will. So calls to a single member state are serialised here, while different member states run in parallel, and transient concurrency errors are retried with exponential backoff. A row that stays throttled through every retry is reported unverifiable and is not charged.

INVALID_REQUESTER_INFO when your own VAT number looks fine

The requester must be an EU member-state VAT number, or a Northern Ireland (XI) one. Great Britain left VIES, so a GB number will not work however valid it is — and a 10-digit UK number is a Unique Taxpayer Reference, a different identifier from a VAT registration number altogether. Supply an unusable requester and VIES answers INVALID_REQUESTER_INFO on every row, which quietly ruins the whole run. This Actor checks the shape before it starts, warns you in the log, and carries on without the requester rather than failing every lookup.

IP_BLOCKED and VAT_BLOCKED are not the same problem

VAT_BLOCKED is scoped to a single number: that row comes back unverifiable with the reason attached, and the run continues normally. IP_BLOCKED is scoped to you, and retrying extends it, so the run aborts immediately with an explanation instead of digging the hole deeper. If you hit it: lower the concurrency, cache results on your side, and contact TAXUD if it persists.

FAQ

Why is companyName empty for a valid German or Spanish VAT number? Those member states do not disclose the registered name and address through VIES, for data-protection reasons. VIES returns ---, which this Actor reports as null with an explanatory message rather than pretending the name is missing or mismatched.

What is a consultation number and why should I store it? It is the identifier VIES returns as evidence that you verified a counterparty on a specific date, relevant under Article 31 of Regulation 904/2010. VIES only issues it when you supply your own VAT number in the request, and it cannot be recovered later. If VAT compliance is the reason you are running this, fill in Your own VAT number and keep the output. The run summary reports how many consultation numbers came back, and the log warns you if you asked for them and got none — so you find out immediately, rather than during an audit.

Does the name-match verdict come from VIES? No. VIES accepts a trader name for comparison, but member states frequently return NOT_PROCESSED rather than a verdict — Ireland does so even when the name supplied is character-for-character correct. So nameMatch and nameMatchScore are computed here, against the registered name VIES returns, after normalising legal-form suffixes, diacritics, punctuation and word order. VIES's own verdict is used only when a member state actually provides one.

Can I use my UK company's tax number to get consultation numbers? No, for two separate reasons. First, a 10-digit number is a Unique Taxpayer Reference (UTR), which is a different identifier from a VAT registration number (9 or 12 digits). Second, even a genuine Great Britain VAT number will not work, because GB left VIES — the requester must be an EU member-state VAT number, or a Northern Ireland (XI) one. If you supply something unusable the Actor says so in the log and carries on without it, rather than letting VIES reject every row.

How many numbers can I check in one run? There is no hard cap, but VIES throttles by concurrency and will block an IP that pushes too hard. The defaults are deliberately conservative. For large recurring lists, cache results on your side and re-check on a schedule rather than re-validating the same numbers repeatedly.

Can it check UK VAT numbers? Great Britain left VIES after Brexit, so live GB lookups require the HMRC Check a UK VAT Number API, which needs your own OAuth credentials (the open v1 endpoint was withdrawn in February 2025). GB numbers are format- and checksum-validated offline and returned as not_supported rather than being wrongly marked invalid. Northern Ireland (XI) is in VIES and is fully supported.

Does a malformed result mean the number is definitely fake? It means the number does not satisfy the published format and check digit for that country. A handful of countries allow a national personal identification number to be used as a VAT number, and those branches are poorly documented, so anything ambiguous is marked formatCheck: "unknown" and sent to VIES for the real answer instead of being rejected.

Is this an official EU service? No. It queries the official European Commission VIES service on your behalf. The Commission obtains data from national databases and does not guarantee its accuracy.

Notes on data protection

VIES is a register of taxable persons, not a consumer database. It is not, however, strictly company-only: several member states (Bulgaria, Czechia, Latvia, Lithuania, Romania, Slovakia) issue VAT numbers to sole traders, and in some of those the number encodes a national personal identification code that embeds a date of birth. Treat stored VAT numbers from those jurisdictions as potentially personal data under the GDPR, and take your own advice on retention.

Support

Open an issue on the Actor's Issues tab. Bug reports with the input that triggered them get fixed fastest.