# Validate EU VAT in Node.js (5 Lines of Code)

# Validate EU VAT in Node.js (5 Lines of Code)

You can validate any EU VAT number in Node.js by calling the EuroValidate REST API. Install the SDK, pass a VAT number, and get back a typed result with the company name, address, and a confidence score -- all in under 5 milliseconds for cached lookups. No SOAP parsing, no XML, no VIES downtime headaches. Here is the minimal working example.

## Quick start

Install the SDK:

```bash
npm install @eurovalidate/sdk
```

Validate a VAT number in 5 lines:

```javascript
import { EuroValidate } from "@eurovalidate/sdk";

const client = new EuroValidate("ev_live_your_key_here");

const result = await client.validateVat("NL820646660B01");
console.log(result.valid, result.companyName);
// true "COOLBLUE B.V."
```

That is it. The SDK handles retries on rate limits (429 with Retry-After), timeouts, and error mapping automatically.

## Using curl instead

If you prefer to test from the terminal before writing any code:

```bash
curl -H "X-API-Key: ev_live_your_key_here" \
  https://api.eurovalidate.com/v1/vat/NL820646660B01
```

## What the API returns

### Valid VAT number

Request: `GET /v1/vat/NL820646660B01`

```json
{
  "vat_number": "NL820646660B01",
  "country_code": "NL",
  "status": "valid",
  "company_name": "COOLBLUE B.V.",
  "company_address": "Weena 664 3012CN ROTTERDAM",
  "request_id": "req_abc123",
  "meta": {
    "confidence": "high",
    "source": "vies_live",
    "cached": false,
    "response_time_ms": 247,
    "last_verified": "2026-04-05T10:15:30Z",
    "upstream_status": "ok"
  }
}
```

### Invalid VAT number

Request: `GET /v1/vat/NL000000000B00`

```json
{
  "vat_number": "NL000000000B00",
  "country_code": "NL",
  "status": "invalid",
  "company_name": null,
  "company_address": null,
  "request_id": "req_def456",
  "meta": {
    "confidence": "high",
    "source": "vies_live",
    "cached": false,
    "response_time_ms": 189,
    "last_verified": "2026-04-05T10:15:35Z",
    "upstream_status": "ok"
  }
}
```

The `status` field is one of: `valid`, `invalid`, `error`, or `unavailable`. The `confidence` field tells you how fresh the data is: `high` means a live upstream check, `medium` means served from cache, `low` means the upstream was down and you are getting stale data.

## TypeScript support

The SDK ships with full type definitions. Every response is a typed interface:

```typescript
import { EuroValidate, VatResult } from "@eurovalidate/sdk";

const client = new EuroValidate("ev_live_your_key_here");

const result: VatResult = await client.validateVat("FR40303265045");

if (result.valid) {
  console.log(`Company: ${result.companyName}`);
  console.log(`Address: ${result.companyAddress}`);
  console.log(`Confidence: ${result.meta?.confidence}`);
} else {
  console.log(`VAT number ${result.vatNumber} is not valid`);
}
```

The `VatResult` interface gives you autocomplete for `valid`, `vatNumber`, `countryCode`, `companyName`, `companyAddress`, `status`, `meta`, and `requestId`.

## Error handling

The SDK throws typed errors you can catch:

```typescript
import { EuroValidate, RateLimitError, AuthError } from "@eurovalidate/sdk";

const client = new EuroValidate("ev_live_your_key_here");

try {
  const result = await client.validateVat("NL820646660B01");
  console.log(result.valid);
} catch (err) {
  if (err instanceof RateLimitError) {
    // Automatic retry handles most 429s, but if retries are exhausted:
    console.log("Rate limited. Try again later.");
  } else if (err instanceof AuthError) {
    console.log("Invalid API key.");
  } else {
    throw err;
  }
}
```

## Latency

Typical response times you can expect:

| Scenario | Latency |
|----------|---------|
| Cached result (Redis hit) | 1--5 ms |
| Live VIES lookup | 150--300 ms |
| VIES unavailable, stale cache returned | 2--8 ms |

Once a VAT number is validated, the result is cached for 24 hours. Subsequent lookups for the same number hit the Redis cache and return in single-digit milliseconds. For high-traffic applications, this means the majority of your requests resolve almost instantly.

## Common pitfalls

### Germany and Spain never return company names

The German (DE) and Spanish (ES) tax authorities do not return trader name or address through VIES. This is a data protection policy at the country level, not a bug. When you validate a German VAT number, `company_name` and `company_address` will be `null` even if the number is valid.

EuroValidate works around this by cross-referencing GLEIF data (the Global LEI system) when available, but coverage depends on whether the company holds an LEI.

### Greece uses EL, not GR

VIES uses the prefix `EL` for Greece, but ISO 3166 uses `GR`. The API accepts both -- you can pass `EL820646660` or `GR820646660` and the mapping is handled automatically. The `country_code` field in the response always returns the ISO code (`GR`).

### Northern Ireland dual status

Northern Ireland businesses may have both a UK VAT number (GB prefix) and an EU EORI number (XI prefix) for customs. If you are handling cross-border e-commerce involving Northern Ireland, validate both the VAT and EORI separately.

### VIES downtime

The VIES service operated by the European Commission has a historical uptime around 70%. Individual country backends go down independently -- Germany alone accounts for the majority of VIES errors. EuroValidate uses per-country circuit breakers so that one country going offline does not affect lookups for other countries. When a country backend is unavailable, the API returns the last cached result with `confidence: "low"` and `upstream_status` indicating the issue.

## Unified validation

If you need to validate a VAT number and an IBAN in a single request, use the unified endpoint:

```typescript
const client = new EuroValidate("ev_live_your_key_here");

const response = await fetch("https://api.eurovalidate.com/v1/validate", {
  method: "POST",
  headers: {
    "X-API-Key": "ev_live_your_key_here",
    "Content-Type": "application/json"
  },
  body: JSON.stringify({
    vat_number: "NL820646660B01",
    iban: "DE89370400440532013000"
  })
});

const data = await response.json();
console.log(data.vat.status);  // "valid"
console.log(data.iban.valid);  // true
```

This counts as one API call against your quota.

## Pricing

The free tier includes 100 requests per hour -- enough to build and test your integration. Paid plans start at EUR 19/month for 5,000 requests.

| Plan | Price | Requests/month |
|------|-------|----------------|
| Free | EUR 0 | 100/hr |
| Starter | EUR 19/mo | 5,000 |
| Growth | EUR 49/mo | 25,000 |
| Scale | EUR 149/mo | 100,000 |

## Next steps

1. [Get your free API key](https://api.eurovalidate.com/v1/auth/signup) -- takes 30 seconds, no credit card required.
2. [Read the full API docs](https://api.eurovalidate.com/docs) -- interactive Swagger UI where you can test every endpoint.
3. [Explore the IBAN endpoint](https://eurovalidate.com) -- validate IBANs and get bank name + BIC in the same call.
4. [Batch validation](https://api.eurovalidate.com/docs#/batch) -- validate up to 1,000 VAT numbers in a single async request.

---

*EuroValidate is a unified REST API that replaces 5 fragmented EU government services (VIES, EORI, IBAN, GLEIF, national registries) with a single JSON endpoint. Built for developers who need reliable EU business data validation without dealing with SOAP/XML, per-country downtime, or inconsistent response formats.*
