npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

vies-checker

v4.0.0

Published

Modern European VIES VAT number validator with full TypeScript support

Readme

VIES Checker

EU API Works

Modern European VIES VAT number validator with full TypeScript support.

Features

  • Native fetch API - Uses Node.js 18+ built-in fetch (no dependencies!)
  • Full TypeScript support - Comprehensive types for all API responses
  • Rich company data - Get company name, address, and validation details
  • Robust error handling - Custom error class with helper methods
  • Timeout & retry support - Configurable timeouts and automatic retries
  • Input normalization - Automatically handles spaces, dashes, and case
  • ESM & CommonJS - Dual module support for maximum compatibility
  • Zero dependencies - Lightweight and secure

Requirements

  • Node.js >= 18.0.0 (for native fetch API support)

Installation

npm install vies-checker
# or
pnpm add vies-checker
# or
yarn add vies-checker

Quick Start

Basic Validation with Full Details

import { checkVAT } from 'vies-checker';

const result = await checkVAT('LU', '26375245');

console.log(result.isValid);     // true
console.log(result.name);        // "AMAZON EUROPE CORE S.A R.L."
console.log(result.address);     // "38, AVENUE JOHN F. KENNEDY\nL-1855  LUXEMBOURG"
console.log(result.requestDate); // Date object

Simple Boolean Check

import { isValid } from 'vies-checker';

const valid = await isValid('LU', '26375245');
console.log(valid); // true

API Reference

checkVAT(country, vatNumber, options?)

Validates a VAT number and returns comprehensive company information.

Parameters:

  • country (string) - Two-letter EU country code (e.g., 'LU', 'DE', 'FR', 'IT')
  • vatNumber (string) - VAT number to validate (spaces/dashes will be removed automatically)
  • options (object, optional)
    • timeout (number) - Request timeout in milliseconds (default: 10000)
    • retries (number) - Number of retry attempts on network errors (default: 0)

Returns: Promise<ViesCheckResult>

interface ViesCheckResult {
  isValid: boolean;
  requestDate: Date;
  country: EuropeanMemberState;
  vatNumber: string;
  name?: string;              // Company name (if valid)
  address?: string;           // Company address (if valid)
  approximateMatch?: object;  // Approximate matching details
}

Throws: ViesError - When the VIES service returns an error state

Examples:

// Basic usage
const result = await checkVAT('LU', '26375245');

// With timeout
const result = await checkVAT('LU', '26375245', { timeout: 5000 });

// With retries
const result = await checkVAT('LU', '26375245', { retries: 2 });

// Input is normalized automatically
const result = await checkVAT('lu', '26 37 52 45'); // Works fine!

isValid(country, vatNumber)

⚠️ Deprecated - Consider using checkVAT() for richer response data.

Simple boolean validation check for backward compatibility.

Parameters:

  • country (EuropeanMemberState) - Two-letter EU country code
  • vatNumber (string) - VAT number to validate

Returns: Promise<boolean | null>

  • true - VAT number is valid
  • false - VAT number is invalid or network error occurred
  • null - Missing country or VAT number

Throws: ViesError - On service errors (rate limits, unavailable, etc.)

Example:

const valid = await isValid('LU', '26375245');
console.log(valid); // true

Response Format

The checkVAT() function returns a comprehensive result object:

{
  isValid: boolean;           // true if VAT is valid in VIES
  requestDate: Date;          // When the validation was performed
  country: string;            // Country code (e.g., 'LU')
  vatNumber: string;          // Validated VAT number
  name?: string;              // Company name (only if valid)
  address?: string;           // Full company address (only if valid)
  approximateMatch?: {        // Approximate matching details
    name: string;
    street: string;
    postalCode: string;
    city: string;
    companyType: string;
    matchName: 1 | 2 | 3;     // 1=valid, 2=invalid, 3=not processed
    matchStreet: 1 | 2 | 3;
    matchPostalCode: 1 | 2 | 3;
    matchCity: 1 | 2 | 3;
    matchCompanyType: 1 | 2 | 3;
  }
}

Error Handling

The package uses a custom ViesError class for API-related errors:

import { checkVAT, ViesError } from 'vies-checker';

try {
  const result = await checkVAT('LU', '26375245');
  console.log(result.name);
} catch (error) {
  if (error instanceof ViesError) {
    console.log('Error code:', error.code);
    console.log('Error message:', error.message);

    // Check error type
    if (error.isTransient()) {
      console.log('Temporary error, retry might work');
    }

    if (error.isRateLimitError()) {
      console.log('Rate limit hit, slow down requests');
    }
  }
}

Error Codes

| Error Code | Description | Transient? | Action | |-----------|-------------|------------|--------| | VALID | VAT number is valid | N/A | Success | | INVALID | VAT number is invalid | N/A | Expected | | INVALID_INPUT | Input validation failed | No | Fix input format | | GLOBAL_MAX_CONCURRENT_REQ | Too many concurrent requests globally | Yes | Wait and retry | | MS_MAX_CONCURRENT_REQ | Too many concurrent requests for member state | Yes | Wait and retry | | SERVICE_UNAVAILABLE | VIES service is unavailable | Yes | Retry later | | MS_UNAVAILABLE | Member state service unavailable | Yes | Retry later | | TIMEOUT | Request timed out | Yes | Retry with longer timeout |

ViesError Helper Methods

  • error.isTransient() - Returns true if error is temporary and worth retrying
  • error.isRateLimitError() - Returns true if error is due to rate limiting

Advanced Usage

Timeout Configuration

// Set a custom timeout (in milliseconds)
const result = await checkVAT('LU', '26375245', {
  timeout: 5000  // 5 seconds
});

Retry Logic

// Automatically retry on network errors
const result = await checkVAT('LU', '26375245', {
  retries: 2  // Retry up to 2 times
});

Combined Options

const result = await checkVAT('LU', '26375245', {
  timeout: 15000,  // 15 second timeout
  retries: 3       // Retry 3 times on failure
});

TypeScript Integration

Full TypeScript support with exported types:

import {
  checkVAT,
  ViesCheckResult,
  ViesError,
  EuropeanMemberState
} from 'vies-checker';

async function validateCompany(
  country: EuropeanMemberState,
  vat: string
): Promise<ViesCheckResult> {
  return await checkVAT(country, vat);
}

// Type-safe error handling
try {
  const result = await validateCompany('LU', '26375245');
  console.log(result.name);
} catch (error) {
  if (error instanceof ViesError && error.isTransient()) {
    // Retry logic
  }
}

Input Normalization

The package automatically normalizes inputs for convenience:

// All of these work:
await checkVAT('LU', '26375245');
await checkVAT('lu', '26375245');      // Lowercase country
await checkVAT('LU', '26 37 52 45');   // Spaces
await checkVAT('LU', '26-37-52-45');   // Dashes
await checkVAT('LU', '26.37.52.45');   // Dots

// All normalize to: country='LU', vatNumber='26375245'

Supported Countries

All 27 EU member states plus Northern Ireland:

AT, BE, BG, CY, CZ, DE, DK, EE, EL (Greece), ES, FI, FR, HR, HU, IE, IT, LT, LU, LV, MT, NL, PL, PT, RO, SE, SI, SK, XI (Northern Ireland)

Migration from v3.x

Breaking Changes

  • Node.js 18+ required (v3.x worked on any Node.js version)
  • TypeScript 5.x recommended (v3.x used TypeScript 4.x)

Non-Breaking Changes

  • The isValid() function works exactly the same way
  • All imports remain compatible
  • Return values are unchanged

Recommended Migration

For new code, use checkVAT() to access rich company data:

// v3.x
const valid = await isValid('LU', '26375245');

// v4.x - same as v3.x (still works!)
const valid = await isValid('LU', '26375245');

// v4.x - recommended (get more data!)
const result = await checkVAT('LU', '26375245');
console.log(result.isValid);  // same boolean
console.log(result.name);     // NEW: company name
console.log(result.address);  // NEW: company address

Examples

Validate and Display Company Info

import { checkVAT } from 'vies-checker';

const result = await checkVAT('LU', '26375245');

if (result.isValid) {
  console.log(`✅ Valid VAT`);
  console.log(`Company: ${result.name}`);
  console.log(`Address: ${result.address}`);
} else {
  console.log(`❌ Invalid VAT`);
}

Batch Validation with Error Handling

import { checkVAT, ViesError } from 'vies-checker';

const companies = [
  { country: 'LU', vat: '26375245' },
  { country: 'DE', vat: '12345678' },
  { country: 'FR', vat: '98765432' },
];

for (const company of companies) {
  try {
    const result = await checkVAT(company.country, company.vat, {
      timeout: 10000,
      retries: 2
    });

    console.log(`${company.country}${company.vat}: ${result.isValid ? '✅' : '❌'}`);
    if (result.name) {
      console.log(`  → ${result.name}`);
    }
  } catch (error) {
    if (error instanceof ViesError) {
      console.log(`${company.country}${company.vat}: Error (${error.code})`);

      if (error.isTransient()) {
        console.log('  → Will retry later...');
      }
    }
  }
}

Retry on Transient Errors

import { checkVAT, ViesError } from 'vies-checker';

async function validateWithRetry(country: string, vat: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await checkVAT(country, vat, { timeout: 10000 });
    } catch (error) {
      if (error instanceof ViesError && error.isTransient() && attempt < maxRetries) {
        console.log(`Attempt ${attempt} failed, retrying...`);
        await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2s
        continue;
      }
      throw error;
    }
  }
}

const result = await validateWithRetry('LU', '26375245');

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

MIT © Iñigo Taibo

Links