vatverify home
open source · MIT

Real algorithms.
No API key required.

Format, checksum, and rate lookup for 44 European countries. Open source, zero runtime dependencies, MIT licensed. The same checksum code that runs inside the vatverify API, published for you to read and use.

Usage

One import, no setup.

No API key, no client, no network. The rate data ships inside the package as a plain JSON file, updated daily at source and republished on every change.

install
npm install @vatverify/vat-rates
validate.ts
import {  validate,  getStandardRate,  isEUMember,} from "@vatverify/vat-rates"// Real checksum: MOD11 for DE, MOD97 for FR, Luhn for IT, ...validate("DE811569869")// { valid: true }validate("DE811569868")// { valid: false, errors: ["invalid checksum"] }getStandardRate("DE")   // 19getStandardRate("GB")   // 20getStandardRate("CH")   // 7.7isEUMember("DE")        // trueisEUMember("GB")        // false (type-narrows to EUMemberCode)
Library vs. API

Use the library. Upgrade when you need a registry.

The library answers “is this the right shape + a valid checksum?”. The API answers “is this VAT number actually registered at the tax authority?”. Most production systems use both.

CapabilityLibraryAPI
  • Format validation (regex)
  • Checksum algorithm (MOD11, MOD97, Luhn, HMRC 97-55)
  • VAT rate lookup (standard, reduced, super-reduced)
  • Country metadata (name, flag, EU membership)
  • Works offline, zero network calls
  • Live registry check (VIES, HMRC, BFS, Brreg)
  • Registered company name + address
  • Tax decision engine (/v1/decide)
  • Webhooks, batch, SLA
Real-world

Cheap gate, expensive check.

Use the library as a zero-cost pre-filter on a Stripe webhook. Most garbage fails the checksum and never touches the API. You only spend a quota request on the inputs that might actually be real.

app/api/stripe/route.ts
// Stripe webhook: cheap format + checksum gate// before spending an API call on live verification.import { validate } from "@vatverify/vat-rates"import { Vatverify } from "@vatverify/node"const vatverify = new Vatverify()export async function POST(req: Request) {  const event = await req.json()  const vat = event.data.object.customer_tax_ids?.[0]?.value  if (!vat) return new Response(null, { status: 204 })  // 1. Offline: zero cost, zero latency, zero network.  const offline = validate(vat)  if (!offline.valid) {    await flagInvalidVat(event.data.object.id, offline.errors)    return new Response(null, { status: 200 })  }  // 2. Only now hit the live API for registry verification.  const live = await vatverify.validate(vat)  if (!live.valid) {    await flagInvalidVat(event.data.object.id, ["not registered"])  }  return new Response(null, { status: 200 })}

Related: /v1/validate reference · API docs.

Why it's open source

Checksum code should be readable, not magic.

The national VAT checksum algorithms are public specifications: MOD11 for Germany, MOD97 for France, Luhn for Italy, the HMRC 97-55 rule for the UK. There's no competitive advantage in hiding them, and shipping them as a black box inside an API is the wrong default.

Publishing the library does three things for you: you can read the algorithm before trusting it, you can validate offline for free when you don't need a live registry check, and you have a fallback the day our API is the one having a bad afternoon. Our live service is paid because the expensive part is running registry-facing infrastructure at 99.9%, not because the math is hard.

The library is MIT licensed with no strings and no telemetry. Use it in commercial products, modify it, fork it, vendor it. We'd rather you adopt the library and never pay us than wrap ours in an adapter because ours was locked away.

Offline first. API when you need it.

The library is yours to keep. When you need live registry verification, company names, batch, webhooks, or the tax-rules engine, the API is a few lines away.