VAT Validation API

Part of the VAT Suite service. One request checks a VAT number: first its form (country prefix, length, structure and check digits), then its registration with the national VAT registry of the country, and returns whether the number is currently registered together with the registered name and address. All EU member states, Northern Ireland, the United Kingdom and Norway.

Endpoint

POSThttps://api.bav.com/validate/
GEThttps://api.bav.com/validate/?api_key=YOUR_API_KEY&vat=FR64443061841
  • HTTPS only. POST bodies are application/x-www-form-urlencoded; GET takes the same parameters in the query string. Both methods return the same response. JSON request bodies are not read (result code 306).
  • Responses are JSON, Content-Type: application/json, with non-ASCII characters escaped; the 429 and 500 cases below are the exceptions (HTML and empty bodies). There is no format parameter; one sent is ignored.
  • The HTTP status tells the kind of outcome: 200 the number is well-formed and the registry answered (the outcome is in active); 422 the number failed a format check, with the same body structure and result code 601–604; 400 a parameter is missing (306, 307); 403 an account problem (301–305); 429 when the rate limit is exceeded; 503 when the national registry does not answer.
  • Response times depend on the registry: most answers take 0.1–0.5 s, but some registries are slow (longest recorded per country: DK 15.2 s, CZ 9.4 s, LV 8.7 s, FR 5.0 s, BE 4.0 s, IT 3.0 s, NL 1.6 s, DE 1.4 s). Allow at least 30 s before timing out.
Your first request
  1. Copy your key from API Access.
  2. Run, with the key in place of YOUR_API_KEY:
curl "https://api.bav.com/validate/" -d api_key=YOUR_API_KEY -d vat=FR64443061841

The full response to this request is in the Response section; the same request in six more languages is under Example request.

Keeping the key safe
  • Use POST in production. With GET the key is part of the URL and ends up in proxy, load-balancer and web-server access logs; with POST it travels in the request body.
  • Call the API from your server. A key embedded in browser JavaScript or a mobile app can be read from the source or the network traffic.
  • Restrict the key to the IP addresses of your servers under Settings; requests from anywhere else are refused.
  • Keep the key out of source control; load it from an environment variable or a configuration file. A key cannot be regenerated from the client area: if it is exposed, contact support to have it replaced.

Request parameters

Two parameters; anything else is ignored.

ParameterRequiredValueDescription
api_keyyesstringYour personal key, shown under API Access. Missing: HTTP 400, result code 306. Wrong or empty: HTTP 403, result code 301.
vatyesstringThe VAT number to check, with its country prefix (FR64443061841). Upper or lower case; spaces, full stops and hyphens are removed before the check, so BE 0567.647.463, BE0567-647-463 and fr 64 443 061 841 are read as BE0567647463 and FR64443061841. Any other character gives result code 601. Missing: HTTP 400, result code 307; empty or shorter than 4 characters: HTTP 422, result code 603. Greece is sent as EL (the ISO code GR is refused with 602), Northern Ireland as XI.

Example request

Every example checks the same number, FR64443061841 (Google France); its response is shown in the next section. Replace YOUR_API_KEY with the key from API Access. Each sample sets a 30-second timeout, because a slow registry can take more than ten seconds to answer.

curl "https://api.bav.com/validate/" --max-time 30 \
  -d api_key=YOUR_API_KEY \
  -d vat=FR64443061841

Response

Five members in this order: query, valid, active, registration_data and error. The API sends the body indented with four spaces and with non-ASCII characters as \uXXXX escapes; the examples on this page are re-indented and decoded. The response to the example request:

{
   "query": {
      "vat": "FR64443061841",
      "status": "completed",
      "request_time": "2026-10-09 07:46:39"
   },
   "valid": true,
   "active": true,
   "registration_data": {
      "name": "EURL GOOGLE FRANCE",
      "address": "8 RUE DE LONDRES",
      "city": "PARIS",
      "zip": "75009",
      "country_iso": "FR",
      "country_name": "France"
   },
   "error": {}
}
FieldTypeDescription
query.vatstringThe number as it was checked: upper case, spaces, full stops and hyphens removed. Empty when an empty value was sent.
query.statusstringcompleted in every recorded response. processing is defined for a national registry that does not answer (see Handling errors).
query.request_timestringTime the check was completed, in UTC, YYYY-MM-DD HH:MM:SS; it equals the Date header of the response, so with a slow registry it is up to 15 s after the request was sent.
validbooleantrue when the number passed the checks that need no registry: country prefix, length, structure and check digits. In a 200 response it is always true; a number that fails these checks is answered with HTTP 422 and false.
activebooleantrue when the national registry lists the number as a current VAT registration. false for a well-formed number that the registry does not list as registered, and in every 422 response.
registration_dataobjectThe registration as the registry delivers it, six string fields (below). An empty object {} when active is false.
errorobjectAn empty object {} in a 200 response. In a 422 response, code (a number, 604) and text. See Result codes.
registration_data
FieldDescription
nameRegistered name of the business, in the script and case the registry uses (EURL GOOGLE FRANCE, Nokia Oyj, a Greek name in Greek letters). Non-ASCII characters are escaped as \uXXXX in the JSON text, which every JSON parser decodes.
addressStreet address. May be empty or the literal N/A when the registry has none. Some registries deliver the whole address in one field: Romania fills city and leaves address and zip empty; Northern Ireland (XI) puts the whole address in zip. Store the three fields together.
cityCity; may be empty (see above).
zipPostal code; may be empty, or hold the whole address (XI).
country_isoISO 3166-1 alpha-2 code of the registering country: GR for a number sent with the EL prefix, GB for a number sent with XI.
country_nameCountry name in English (Czechia, United Kingdom).
Cleaned input

BE 0567.647.463 sent as the vat parameter: the number is checked and echoed as BE0567647463. BE0567-647-463 gives the same response; fr 64 443 061 841 is echoed as FR64443061841:

{
   "query": {
      "vat": "BE0567647463",
      "status": "completed",
      "request_time": "2026-10-09 07:46:40"
   },
   "valid": true,
   "active": true,
   "registration_data": {
      "name": "BVBA Waowdeals.be",
      "address": "Uitbreidingstraat 84",
      "city": "Antwerpen",
      "zip": "2600",
      "country_iso": "BE",
      "country_name": "Belgium"
   },
   "error": {}
}
Well-formed but not registered

A number that passes the format and check-digit tests but is not a current VAT registration: HTTP 200, valid true, active false, registration_data empty. A company number that is not a VAT number (an Italian codice fiscale, for instance) is answered the same way:

{
   "query": {
      "vat": "GB999999973",
      "status": "completed",
      "request_time": "2026-10-09 07:59:38"
   },
   "valid": true,
   "active": false,
   "registration_data": {},
   "error": {}
}
Greece

Sent with the EL prefix; the name comes back in Greek letters (escaped as \uXXXX in the raw JSON) and country_iso is GR:

{
   "query": {
      "vat": "EL094014201",
      "status": "completed",
      "request_time": "2026-10-09 07:47:12"
   },
   "valid": true,
   "active": true,
   "registration_data": {
      "name": "ΤΡΑΠΕΖΑ ΕΘΝΙΚΗ ΤΗΣ ΕΛΛΑΔΟΣ ΑΝΩΝΥΜΗ ΕΤΑΙΡΕΙΑ",
      "address": "eolou 86",
      "city": "athina",
      "zip": "10559",
      "country_iso": "GR",
      "country_name": "Greece"
   },
   "error": {}
}
Northern Ireland

A UK number sent with the XI prefix. The registry delivers the whole address in one field, here zip, and country_iso is GB:

{
   "query": {
      "vat": "XI220430231",
      "status": "completed",
      "request_time": "2026-10-09 07:47:15"
   },
   "valid": true,
   "active": true,
   "registration_data": {
      "name": "TESCO PLC",
      "address": "",
      "city": "",
      "zip": "CIRRUS A SHIRE PARK KESTREL WAY WELWYN GARDEN CITY   AL7 1GA",
      "country_iso": "GB",
      "country_name": "United Kingdom"
   },
   "error": {}
}
Format failure

HTTP 422 with the full structure; here a wrong check digit (604). Note that error.code is a number and the message is in text:

{
   "query": {
      "vat": "FR64443061842",
      "status": "completed",
      "request_time": "2026-10-09 07:46:32"
   },
   "valid": false,
   "active": false,
   "registration_data": {},
   "error": {
      "code": 604,
      "text": "Invalid VAT format, length, structure or check digit."
   }
}

Greece sent as GR, result code 602:

{
   "query": {
      "vat": "GR094014201",
      "status": "completed",
      "request_time": "2026-10-09 07:47:13"
   },
   "valid": false,
   "active": false,
   "registration_data": {},
   "error": {
      "code": 602,
      "text": "Country code not supported."
   }
}
Request and account errors

HTTP 400 and 403 responses carry only error, with code as a string and the message in message. A missing vat parameter:

{
   "error": {
      "code": "307",
      "message": "Bad Request: Missing 'vat' parameter value"
   }
}

A wrong key (HTTP 403):

{
   "error": {
      "code": "301",
      "message": "API Key is invalid"
   }
}
FieldTypeDescription
error.codestringThe result code, as a three-digit string ("306").
error.messagestringThe message from the code table.

Result codes

The code is in error.code. 6xx codes describe the number and come with HTTP 422, where the code is a number and the text is in error.text. 3xx codes describe the request or the account and come with HTTP 400 or 403, where the code is a string and the text is in error.message. Read the code with a parser that accepts both types.

Number
CodeHTTPTextMeaning
601422Invalid characters in VAT number.After spaces, full stops and hyphens are removed, a character other than a letter or digit is left.
602422Country code not supported.The first two characters are not a prefix the API knows: a country outside the supported list, a number sent without its prefix (64443061841), or Greece sent as GR instead of EL.
603422VAT number cannot be shorter than 4 characters.The value is empty or shorter than four characters.
604422Invalid VAT format, length, structure or check digit.The prefix is known but the rest does not fit that country’s format, or its check digits are wrong. The registry is not consulted.
Request and account

No lookup is performed and no query is used. 301, 306 and 307 were recorded; 302 to 305 follow the same path with their own message.

CodeHTTPMessageWhat to do
301403API Key is invalidCopy the key exactly as shown under API Access; an empty key gives the same answer.
302403Subscription expiredRenew the VAT Suite service under My Services.
303403No queries availableYour query balance is used up; top up under Pricing.
304403You have no access to this APIThe VAT Suite service is not active on this account.
305403IP Address not allowedThe key is restricted to certain IP addresses; add the calling server’s address under Settings.
306400Bad Request: Missing 'api_key' parameter valueSend api_key. Also the answer to a JSON request body, which is not read: send a form body.
307400Bad Request: Missing 'vat' parameter valueSend vat.
Other responses
HTTPBodyMeaning
429HTML pageMore than 20 requests per second; see Rate limits.
500empty, text/htmlA parameter was sent in a form the API cannot read, such as vat[]. Send plain string parameters.
503JSON, result code 503, National registry not responding. Try again later.The registry of the country did not answer; the query is not charged. Retry later. Not observed in the recorded calls.

Handling errors

What each outcome means for your code. The format checks are deterministic; the registration status is not, so do not cache active for long — a registration can be cancelled at any time, and tax authorities expect a check at the time of each supply.

SituationMeaningWhat to do
HTTP 200The number is well-formed and the registry answered. The outcome is active.Do not retry. Store the result with request_time.
HTTP 422, result code 601–604The number failed a check that needs no registry: characters, prefix, length, structure or check digits. The query is charged.Do not retry: the same input gives the same result. Show the user the text, log the code.
HTTP 400, result code 306 or 307A required parameter is missing. Only error is in the body.Fix the request. Do not retry it unchanged.
HTTP 403, result code 301 or 304The key is wrong, or the VAT Suite service is not on the account.Stop sending; every request will fail the same way. Check the key under API Access, or contact support for 304.
HTTP 403, result code 302 or 303The service has expired, or the query balance is used up.Stop sending and alert whoever handles the subscription. Poll the services endpoint to catch this before it happens.
HTTP 403, result code 305The calling address is not on the key’s IP allow-list.Add the address under Settings, then resend.
HTTP 429, HTML bodyMore than 20 requests per second. The request was rejected by the web server before reaching the API: not processed, not charged.Wait a moment and resend; lower the sending rate. Safe to retry. There is no Retry-After header.
HTTP 503, or query.status processingThe national registry did not answer in time. Not charged.Retry the same number later, with increasing delays (30 s, 2 min, 10 min); national registries have maintenance windows.
HTTP 5xx (other), timeout, connection errorTemporary problem on the way to the API or on the API itself.Retry with increasing delays (1 s, 2 s, 4 s, then give up and queue). A request that got no response may still have been processed and charged.

Reading the result

  • Decide on active. valid true only says the number is well-formed; a number that is not registered can be well-formed too (HTTP 200, active false).
  • Decide on the body, not on the HTTP status alone: 200 and 422 both carry the full structure, and a 422 is a final answer about the number, not a request error.
  • Keep the evidence. For a reverse-charge supply store the number as checked (query.vat), request_time, active and the registered name and address with the transaction.
  • Compare the registered name with your customer’s name yourself; the API does not. Expect legal forms and spelling to differ from the trading name (EURL GOOGLE FRANCE, Telefonica sa).
  • Registration data is passed through as the registry delivers it: upper case in some countries, mixed case in others; the whole address in one field in Romania and Northern Ireland; a Greek name in Greek letters.
  • Send the prefix with the number: EL for Greece, XI for Northern Ireland, GB for the rest of the United Kingdom. Spaces, full stops, hyphens and lower case are fine; any other character gives 601.
  • Each check uses one query whether the number is registered or not, including a number refused with 601–604, so check the prefix and the length on your side before sending.

Supported countries

The 27 EU member states, Northern Ireland, the United Kingdom and Norway. 29 of the 30 prefixes were looked up successfully during recording (marked); the format column is the one published by the national registries. A prefix outside this list is refused with result code 602 (recorded for AU, US and XX); Switzerland is the exception, see below.

PrefixCountryFormat after the prefixLookup
ATAustriaU + 8 digits (ATU99999999) recorded
BEBelgium10 digits, first digit 0 or 1 recorded, up to 4.0 s
BGBulgaria9 or 10 digits recorded
CYCyprus8 digits + 1 letter recorded
CZCzechia8, 9 or 10 digits recorded, up to 9.4 s
DEGermany9 digits recorded, up to 1.4 s
DKDenmark8 digits recorded, up to 15.2 s
EEEstonia9 digits recorded
ELGreece9 digits (prefix EL, not GR) recorded
ESSpain9 characters; the first, the last or both are letters recorded
FIFinland8 digits recorded
FRFrance2 characters + 9 digits (SIREN) recorded, up to 5.0 s
HRCroatia11 digits (OIB) recorded
HUHungary8 digits recorded
IEIreland8 or 9 characters with one or two letters: the last, the second and the last, or the last two recorded
ITItaly11 digits recorded, up to 3.0 s
LTLithuania9 or 12 digits recorded
LULuxembourg8 digits recorded
LVLatvia11 digits recorded, up to 8.7 s
MTMalta8 digitsnot recorded
NLNetherlands9 digits + B + 2 digits recorded, up to 1.6 s
PLPoland10 digits (NIP) recorded
PTPortugal9 digits (NIF) recorded
RORomania2 to 10 digits recorded
SESweden12 digits, ending in 01 recorded
SISlovenia8 digits recorded
SKSlovakia10 digits recorded
XINorthern Ireland9 or 12 digits (a UK number with the XI prefix) recorded
GBUnited Kingdom9 or 12 digits, or GD / HA + 3 digits recorded
NONorway9 digits (organisation number) recorded

Switzerland is not available: the prefix is recognised, but a Swiss number in any spelling (CHE-116.281.710, with or without MWST, CH116281710) is refused with 604.

Checking your balance

The services endpoint returns every service on your account with its remaining queries and expiry date, the same information as My Services. Poll it from your monitoring to catch a low balance before the API starts answering 303. It takes your api_key and a format (json or xml) and does not use a query.

GEThttps://api.iban.com/clients/api/v4/services/?api_key=YOUR_API_KEY&format=json
GEThttps://api.iban.com/clients/api/v4/services/?api_key=YOUR_API_KEY&format=xml

The response, shortened to the entry of this service (the endpoint lists every service on your account, one object each):

{
   "services": [
      {
         "service_name": "VAT Suite (2,000)",
         "credits": 1968,
         "expiry_date": "2027-10-09",
         "service_expired": 0
      }
   ],
   "errors": []
}

The HTTP status is 200 in every case. With a wrong key, services is empty and errors carries code 301 ({"services":[],"errors":[{"code":"301","message":"API Key is invalid"}]}); without format the body is empty.

FieldTypeDescription
service_namestringName of the service as shown under My Services. The VAT Suite entry carries the plan size in brackets: VAT Suite (2,000). Match on the prefix VAT Suite.
creditsnumber or stringRemaining queries. Services without a quota return the string Unlimited instead of a number, so test the type before comparing.
expiry_datestringExpiry date of the service, YYYY-MM-DD. Requests after this date are refused as expired.
service_expired0 or 11 when the service has expired.

Rate limits and queries

  • The shared API accepts up to 20 requests per second. Beyond that, requests are rejected with HTTP 429 Too Many Requests and a small HTML body; a rejected request never reaches the API and is not charged. Acceptance resumes as soon as your rate drops below the limit, so wait a moment and resend; keep the sending rate at or below 20 per second with a queue or a token bucket. If several processes send requests, apply the limit to their total.
  • Each processed check uses one query from your VAT Suite balance, whatever the outcome (HTTP 200 or 422). Requests refused with 400, 403, 429 or 500 use none; neither does a registry that does not answer (503). Your balance and expiry date are shown under My Services and by the services endpoint.
  • Need more throughput? A dedicated API server is available for your account with no per-second limit, on its own hostname. Contact support with your expected volume.

Glossary

TermMeaning
VAT numberThe identifier a business is registered under for value-added tax, also called VAT ID or VAT registration number: a two-letter country prefix followed by the national number. The national part is often the company number (SIREN in France, CVR in Denmark, organisation number in Sweden and Norway) or a tax number (NIP, NIF).
PrefixThe first two characters. They follow ISO 3166-1 with two exceptions: Greece uses EL, and Northern Ireland uses XI since 2021 for goods traded under the Northern Ireland Protocol.
Check digitOne or more digits computed from the rest of the number by the issuing country’s algorithm (modulus 97 in Belgium and France, ISO 7064 MOD 11,10 in Germany, modulus 11 in the Netherlands, Luhn in Italy and Sweden). A wrong check digit gives result code 604 without a registry lookup.
Valid / activevalid means well-formed; active means currently registered. A number can be well-formed and not registered, so use active for decisions.
Registration dataThe name and address under which the number is registered, as delivered by the national registry. Format, case and script are the registry’s.
VIESVAT Information Exchange System, the European Commission’s system through which the EU member states and Northern Ireland publish the registration status of their VAT numbers.
Reverse chargeIn business-to-business supplies across EU borders the customer, not the supplier, accounts for the VAT; the supplier must hold a valid VAT number of the customer at the time of the supply, which is the usual reason to call this API and to keep the result with its request_time.