Free & Open Source — No API Key Required

EU VAT Rates Data

Rates for 44 European countries — plus VAT number format descriptions and built-in format validation for every country. Sourced daily from the European Commission TEDB. MIT licensed.

44 CountriesMIT LicenseUpdated DailyEU-27 + CH · NO · GB · UA…TypeScript TypesVAT Format Validator Built-In

EU VAT Rates 2026

Standard, reduced, and super-reduced rates for all 27 EU member states. Data sourced daily from the European Commission TEDB. Last updated: 30.03.2026.

CountryStandardReducedSuper Reduced
ATAustria20%10%, 13%, 19%
BEBelgium21%6%, 12%
BGBulgaria20%9%
HRCroatia25%5%, 13%
CYCyprus19%5%, 9%3%
CZCzech Republic21%12%
DKDenmark25%
EEEstonia24%9%, 13%
FIFinland25.5%10%, 13.5%
FRFrance20%0.9%, 1.05%, 5.5%, 8.5%, 10%, 13%2.1%
DEGermany19%7%
GRGreece24%6%, 13%, 17%4%
HUHungary27%5%, 18%
IEIreland23%9%, 13.5%
ITItaly22%5%, 10%4%
LVLatvia21%5%, 12%
LTLithuania21%5%, 12%
LULuxembourg17%8%, 14%3%
MTMalta18%5%, 7%
NLNetherlands21%9%
PLPoland23%5%, 8%8%
PTPortugal23%6%, 13%, 16%, 22%6%
RORomania21%11%
SKSlovakia23%5%, 19%
SISlovenia22%5%, 9.5%
ESSpain21%10%4%
SESweden25%6%, 12%

Rates are indicative. Some countries apply additional territorial variants (e.g. French overseas departments, Portuguese Azores). Always verify with the relevant tax authority for invoicing purposes.

Unique feature — not available in any other VAT rates package

VAT Number Formats & Validation

Every country includes a human-readable format description and a regex pattern for local format validation. Use the built-in validateFormat() function — no API key, no network call.

import { validateFormat } from 'eu-vat-rates-data'
validateFormat('ATU12345678') // true
validateFormat('DE123456789') // true
validateFormat('INVALID')     // false
CountryFormatPattern
ALAlbaniaAL + 1 letter + 8 digits + 1 letter^AL[A-Z]\d{8}[A-Z]$
ADAndorraAD + alphanumericno standard format
ATAustriaATU + 8 digits^ATU\d{8}$
BEBelgiumBE + 0/1 + 9 digits^BE[01]\d{9}$
BABosnia and HerzegovinaBA + alphanumericno standard format
BGBulgariaBG + 9–10 digits^BG\d{9,10}$
HRCroatiaHR + 11 digits^HR\d{11}$
CYCyprusCY + 8 digits + 1 letter^CY\d{8}[A-Z]$
CZCzech RepublicCZ + 8–10 digits^CZ\d{8,10}$
DKDenmarkDK + 8 digits^DK\d{8}$
EEEstoniaEE + 9 digits^EE\d{9}$
FIFinlandFI + 8 digits^FI\d{8}$
FRFranceFR + 2 alphanumeric + 9 digits^FR[A-HJ-NP-Z0-9]{2}\d{9}$
GEGeorgiaGE + alphanumericno standard format
DEGermanyDE + 9 digits^DE\d{9}$
GRGreeceEL + 9 digits^EL\d{9}$
HUHungaryHU + 8 digits^HU\d{8}$
ISIcelandIS + 5–6 digits^IS\d{5,6}$
IEIrelandIE + 7 digits + 1–2 letters^IE\d{7}[A-W][A-IW]?$|^IE\d[A-Z+*]\d{5}[A-W]$
ITItalyIT + 11 digits^IT\d{11}$
XKKosovoXK + alphanumericno standard format
LVLatviaLV + 11 digits^LV\d{11}$
LILiechtensteinLI + alphanumericno standard format
LTLithuaniaLT + 9 or 12 digits^LT(\d{9}|\d{12})$
LULuxembourgLU + 8 digits^LU\d{8}$
MTMaltaMT + 8 digits^MT\d{8}$
MDMoldovaMD + alphanumericno standard format
MCMonacoMC + alphanumericno standard format
MEMontenegroME + 8 digits^ME\d{8}$
NLNetherlandsNL + 9 digits + B + 2 digits^NL\d{9}B\d{2}$
MKNorth MacedoniaMK + 13 digits^MK\d{13}$
NONorwayNO + 9 digits + MVA^NO\d{9}MVA$
PLPolandPL + 10 digits^PL\d{10}$
PTPortugalPT + 9 digits^PT\d{9}$
RORomaniaRO + 2–10 digits^RO\d{2,10}$
RSSerbiaRS + 9 digits^RS\d{9}$
SKSlovakiaSK + 10 digits^SK\d{10}$
SISloveniaSI + 8 digits^SI\d{8}$
ESSpainES + letter/digit + 7 digits + letter/digit^ES[A-Z0-9]\d{7}[A-Z0-9]$
SESwedenSE + 12 digits^SE\d{12}$
CHSwitzerlandCHE + 9 digits (+ MWST/TVA/IVA)^CHE\d{9}(MWST|TVA|IVA)?$
TRTurkeyTR + 10 digits^TR\d{10}$
UAUkraineUA + 12 digits^UA\d{12}$
GBUnited KingdomGB + 9 or 12 digits (or GD/HA + 3 digits)^GB(\d{9}|\d{12}|GD\d{3}|HA\d{3})$

Note: Greece uses the "EL" prefix in VAT numbers, not "GR". Format validation checks the local format only — use the vatnode API to verify against the official VIES database.

Quick Start

Pick your language and copy-paste to get started.

Install
npm install eu-vat-rates-data
import { getRate, getStandardRate, getAllRates, isEUMember, validateFormat, dataVersion } from 'eu-vat-rates-data'

// Get full record for a country
const fi = getRate('FI')
console.log(fi.standard)       // 25.5
console.log(fi.reduced)        // [10,13.5]
console.log(fi.eu_member)      // true
console.log(fi.vat_abbr)       // 'ALV'
console.log(fi.format)         // 'FI + 8 digits'
console.log(fi.pattern)        // '^FI\d{8}$'

// Shorthand for the standard rate
const de = getStandardRate('DE') // 19

// Iterate all 44 countries
const all = getAllRates()
for (const [code, rate] of Object.entries(all)) {
  console.log(`${code}: ${rate.standard}%`)
}

// VAT number format validation — free, no API key
validateFormat('FI12345678')  // true
validateFormat('INVALID')     // false

// Type guards
isEUMember('NO')        // false — Norway is in dataset but not EU
isEUMember('DE')        // true

// Dataset freshness
console.log(`Rates as of ${dataVersion}`) // 2026-03-30

Everything you need, nothing you don't

A self-contained dataset — no network calls, no rate limits, no account.

44 Countries

EU-27 plus Norway, Switzerland, UK, Turkey, Ukraine and 10 more non-EU European countries.

6 Languages

Native packages for JavaScript/TypeScript, Python, PHP, Go, Ruby, and a WordPress plugin.

Updated Daily

GitHub Actions fetches fresh rates from the EC TEDB every morning and publishes automatically.

TypeScript Types

Full type safety with CountryCode, VatRate, and helper type guards included.

VAT Format Validation

Every country includes a format description and regex pattern. Built-in validateFormat() — free, no API key.

What's in the dataset

Each country record contains the following fields.

standardStandard VAT rate in percent (e.g. 25.5)
reducedReduced rates, sorted ascending. Empty array when none.
super_reducedSuper-reduced rate applied to a narrow set of essentials.
parkingTransitional parking rate. Null for most countries.
eu_memberWhether this is an EU-27 member state.
currencyISO 4217 currency code (EUR for eurozone).
vat_nameLocal name of the tax (e.g. 'Arvonlisävero').
vat_abbrShort abbreviation (e.g. 'ALV', 'MwSt', 'TVA').
formatHuman-readable VAT number format (e.g. 'ATU + 8 digits'). Unique to this package.unique
patternRegex string for VAT number format validation (e.g. '^ATU\\d{8}$'). Null for countries without a standard format. Unique to this package.unique
Sample — Finland (FI)
{
  "country": "Finland",
  "currency": "EUR",
  "eu_member": true,
  "vat_name": "Arvonlisävero",
  "vat_abbr": "ALV",
  "standard": 25.5,
  "reduced": [
    10,
    13.5
  ],
  "super_reduced": null,
  "parking": null,
  "format": "FI + 8 digits",
  "pattern": "^FI\\d{8}$"
}

Available packages

All packages ship the same dataset — pick the one for your stack.

TypeScript

JavaScript / TypeScript

npm install eu-vat-rates-data
Python

Python

pip install eu-vat-rates-data
PHP

PHP

composer require vatnode/eu-vat-rates-data
Go

Go

go get github.com/vatnode/eu-vat-rates-data-go
Ruby

Ruby

gem install eu_vat_rates_data
WordPress / WooCommerce

WordPress / WooCommerce

Canonical dataset repo

Raw JSON data, update scripts, and GitHub Actions workflow for all packages.

vatnode/eu-vat-rates-data

Need VAT number validation too?

The vatnode API validates EU VAT numbers against the official VIES database — real-time, with caching and webhook support.