Tutorial7 min readAlberto García

VAT API Node.js: Quick Start Guide for Backend Developers

The shortest path from zero to a working VAT validation call in Node.js. No extra dependencies needed on Node 18+ — just fetch, your API key, and 20 lines of code.

nodejsvatapiquickstart

This guide gets you from zero to a working VAT number validation in Node.js as fast as possible. No new npm packages required on Node.js 18+ — built-in fetch handles the HTTP call. If you need the full production-ready implementation with caching, a service class, and a complete test suite, see the Node.js EU VAT validation tutorial. This guide is deliberately minimal: get the first call working, understand the response, then add the right error handling.

Note

You need a TaxID API key to follow this guide. Get one free at taxid.dev/signup — the free tier includes 100 validations/month, no credit card required.

Step 1: Add Your API Key

Store your key as an environment variable. Never hardcode it in source files or commit it to version control — treat it like a database password.

bash.env
TAXID_API_KEY=vat_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
javascriptload-env.js
// Node.js 20.6+ supports --env-file natively:
// node --env-file=.env server.js

// Older versions: use dotenv
// npm install dotenv
// require('dotenv').config();

Step 2: Your First Validation Call

The TaxID API uses a simple REST pattern: `GET /api/v1/validate/:country/:vat`. The country code is the two-letter ISO prefix that appears at the start of every VAT number — for `DE123456789`, the country is `DE`.

javascriptvalidate-vat.js
const BASE = 'https://taxid.dev/api/v1';

async function validateVat(vatNumber) {
  const country = vatNumber.slice(0, 2).toUpperCase();
  const vat = vatNumber.replace(/\s/g, '').toUpperCase();

  const res = await fetch(`${BASE}/validate/${country}/${vat}`, {
    headers: { Authorization: `Bearer ${process.env.TAXID_API_KEY}` },
    signal: AbortSignal.timeout(5000),
  });

  if (!res.ok) throw new Error(`API error ${res.status}`);
  return res.json();
}

// Try it:
const result = await validateVat('DE123456789');
console.log(result);

Step 3: Understanding the Response

Every validation response has the same shape. The `status` field is the one that drives all your application logic — not `valid`, which is just a boolean shorthand for `status === 'active'`.

FieldTypeDescription
validbooleantrue only when status is 'active'
statusstringOne of: active, inactive, format_invalid, service_unavailable
company_namestring | nullRegistered company name from the tax authority
addressstring | nullRegistered address from the tax authority
country_codestringTwo-letter country code (e.g. DE, FR, GB)
cachedbooleantrue if result was served from TaxID cache
request_idstringUnique ID for this validation — log it for audit

Step 4: Handle All Four Status Codes

There are exactly four status values. Each requires different application logic. The most common mistake is treating `service_unavailable` the same as `inactive` — do not zero-rate based on an unavailable response.

javascripthandle-vat.js
async function handleVatCheck(vatNumber) {
  let data;
  try {
    data = await validateVat(vatNumber);
  } catch {
    // Network error or timeout — treat same as service_unavailable
    return { outcome: 'unavailable' };
  }

  switch (data.status) {
    case 'active':
      return {
        outcome: 'valid',
        companyName: data.company_name,
        address: data.address,
      };
    case 'inactive':
      return { outcome: 'invalid', reason: 'not_registered' };
    case 'format_invalid':
      return { outcome: 'invalid', reason: 'bad_format' };
    case 'service_unavailable':
      // VIES is down — charge standard VAT, re-validate later
      return { outcome: 'unavailable' };
    default:
      return { outcome: 'unavailable' };
  }
}

Warning

When `status` is `service_unavailable`, never silently apply zero-rate VAT. The safest approach is to charge standard VAT and issue a credit note once VIES recovers and confirms the number is valid.

Adding a Validation Endpoint in Express

In a typical Express.js application, expose VAT validation as a POST endpoint your frontend can call. Keep the API key on the server — never pass it to the browser.

javascriptroutes/vat.js
import express from 'express';
const router = express.Router();

router.post('/validate-vat', async (req, res) => {
  const { vatNumber } = req.body;
  if (!vatNumber || typeof vatNumber !== 'string') {
    return res.status(400).json({ error: 'vat_number_required' });
  }

  const result = await handleVatCheck(vatNumber.trim());

  if (result.outcome === 'invalid') {
    return res.status(422).json({
      error: 'invalid_vat',
      reason: result.reason,
    });
  }

  return res.json({
    valid: result.outcome === 'valid',
    companyName: result.companyName ?? null,
    unavailable: result.outcome === 'unavailable',
  });
});

export default router;

TypeScript Version

For TypeScript projects, add types to the response and the handler return value to get full IDE autocompletion and type safety throughout your application.

typescriptlib/vat.ts
type VatStatus = 'active' | 'inactive' | 'format_invalid' | 'service_unavailable';

interface VatResponse {
  valid: boolean;
  status: VatStatus;
  company_name: string | null;
  address: string | null;
  country_code: string;
  cached: boolean;
  request_id: string;
}

export async function validateVat(vatNumber: string): Promise<VatResponse> {
  const country = vatNumber.slice(0, 2).toUpperCase();
  const vat = vatNumber.replace(/\s/g, '').toUpperCase();

  const res = await fetch(
    `https://taxid.dev/api/v1/validate/${country}/${vat}`,
    {
      headers: { Authorization: `Bearer ${process.env.TAXID_API_KEY!}` },
      signal: AbortSignal.timeout(5000),
    }
  );

  if (!res.ok) throw new Error(`TaxID API ${res.status}`);
  return res.json() as Promise<VatResponse>;
}

Start validating EU VAT numbers

Free plan — 100 validations/month. No credit card required.

AG
Alberto García

Founder, TaxID

Building EU VAT validation tools for developers. Obsessed with compliance automation and developer experience.