DEV Community

Daniel Igel
Daniel Igel

Posted on

EU VAT number validation in 3 lines of code: REST API backed by official VIES

EU SaaS checkouts often need to verify a customer's VAT number before applying B2B tax exemption. Hitting VIES directly means dealing with an inconsistent REST response — and doing that per-checkout adds latency you don't want.

GET /api/v1/validate?vat=DE811569869 forwards the number to the official VIES REST API (ec.europa.eu), validates it against all 27 EU member states plus Northern Ireland (XI), and returns clean JSON:

curl --request GET \
  --url 'https://eu-vat-number-validator-api.p.rapidapi.com/api/v1/validate?vat=DE811569869' \
  --header 'x-rapidapi-key: YOUR_RAPIDAPI_KEY' \
  --header 'x-rapidapi-host: eu-vat-number-validator-api.p.rapidapi.com'
Enter fullscreen mode Exit fullscreen mode

Response includes valid, countryCode, vatNumber, and — where the member state exposes it — the registered name and address. Results are cached for 1 hour so repeated lookups on the same number return instantly without hitting VIES again.

Validating a batch on invoice import? POST /api/v1/validate/batch takes up to 10 numbers at once:

const res = await fetch(
  'https://eu-vat-number-validator-api.p.rapidapi.com/api/v1/validate/batch',
  {
    method: 'POST',
    headers: {
      'x-rapidapi-key': process.env.RAPIDAPI_KEY,
      'x-rapidapi-host': 'eu-vat-number-validator-api.p.rapidapi.com',
      'content-type': 'application/json',
    },
    body: JSON.stringify({ vatNumbers: ['DE811569869', 'FR40303265045'] }),
  }
);
const { count, results } = await res.json();
Enter fullscreen mode Exit fullscreen mode

Each item in results carries its own valid flag and an error field if VIES couldn't be reached for that number — one unreachable country doesn't block the rest of the batch.

Free tier on RapidAPI: https://rapidapi.com/danieligel/api/eu-vat-number-validator-api

Do you check VAT numbers at checkout time, or only when generating invoices?

Top comments (0)