Iurii RoguliaIurii Rogulia
AboutServicesPricingProjectsStackReviewsPhrasesBlog
Contact
Iurii ships.

Iurii Rogulia, IT partner for business & fractional CTO. Professionally building software since 2001.

Think of a number
PricingQuality checklistPrivacy PolicyCookie Policy

Business

TMI Iurii Rogulia
VAT ID: FI29845875
DUNS: 368664211
Lappeenranta, Finland 🇫🇮

[email protected]
Back to projects

Iurii Publishes: eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset

February 25, 2026

Free, open-source EU VAT rates for all 27 member states + UK. Published as native packages for npm, PyPI, Packagist, Go, and RubyGems. Auto-updated daily from the official European Commission TEDB API — no API keys, no subscriptions, no paywalls.

Source code

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation

Key Results

  • Free forever — no API keys, no rate limits, no paywalls, no vendor to pay $50–200/month
  • Correct VAT rates in any stack — one dataset, five languages (JavaScript, Python, PHP, Go, Ruby)
  • Covers all 28 markets — EU-27 + UK, 4 rate types per country
  • Always current — auto-updated daily from the official European Commission source
  • Auditable — git history is a complete record of every EU VAT rate change
eu-vat-rates-data — Free & Open-Source EU VAT Rates Dataset

npm

PyPI

Packagist

Go

RubyGems

License

Updated

The Business Problem

Every business selling across European markets has to charge the right VAT rate — and those rates differ by country and change over time. The rates themselves are public data the European Commission publishes for free, yet there is no reliable, free, ready-to-use source a developer can drop into a product.

So teams reach for bad options: hardcode the rates and quietly fall out of date, scrape a website that breaks, or pay a third-party API $50–200/month for numbers that are already public. Every one of those is either a compliance risk or an unnecessary cost.

eu-vat-rates-data is the open-source fix — free, correct VAT rates for every EU market plus the UK, updated daily from the official source, ready to use in whatever language a team already builds in. No registration, no API keys, no usage limits.

The Solution

I built and maintain a free, open dataset of EU VAT rates and published it as a native package for the five most common backend languages — JavaScript/TypeScript, Python, PHP, Go, and Ruby — so any team can install it and get correct rates without changing their stack. It covers all 27 EU member states plus the UK, with every rate type each country uses.

A daily automated job pulls the latest rates straight from the European Commission's official database, so the data stays current with no one having to watch tax legislation. And because every change is committed to a public git history, the project doubles as an auditable record of when each rate changed — useful for anyone who needs to prove which rate applied on a given date.

All source code and data are publicly available at github.com/vatnode.

Results

MetricValue
Packages published5 (npm, PyPI, Packagist, Go, RubyGems)
Countries covered28 (EU-27 + UK)
Rate types4 (standard, reduced, super_reduced, parking)
Update frequencyDaily (automated)
Manual interventionZero (unless EC TEDB changes its API)

Under the Hood

For the technically curious, here is how the dataset is sourced, shaped, and shipped.

Data Source

The dataset is sourced from the European Commission TEDB (Taxes in Europe Database) — the official EU SOAP web service at ec.europa.eu/taxation_customs/tedb/ws/.

Each day, a Python script sends a typed XML request for all 28 countries with situationOn set to today's date, parses the structured response, and writes the result to data/eu-vat-rates-data.json:

soap_body = f"""<v1:retrieveVatRatesReqMsg>
  <types:memberStates>
    {''.join(f'<types:isoCode>{c}</types:isoCode>' for c in COUNTRY_CODES)}
  </types:memberStates>
  <types:situationOn>{today}</types:situationOn>
</v1:retrieveVatRatesReqMsg>"""

Non-obvious edge cases

EL → GR normalization. TEDB uses the EU convention EL for Greece instead of the ISO 3166-1 standard GR. Explicit mapping: TEDB_TO_ISO = {"EL": "GR"}.

UK hardcoded fallback. After Brexit, the UK was removed from TEDB. GB rates (20% standard, 5% reduced) are stored as a static fallback and updated manually when legislation changes.

Non-numeric rate filtering. TEDB returns not just percentages but also EXEMPTED, OUT_OF_SCOPE, NOT_APPLICABLE. All non-numeric values are filtered out — only positive floats make it into the dataset.

Deduplication. Some countries (France, Portugal, Spain) have territorial special rates that appear as duplicate entries in the SOAP response. Rates are aggregated into a set() before being written.

SOAP namespace stripping. XML tags arrive with full namespaces: {urn:...}vatRateResults. Stripped explicitly: el.tag.split("}")[-1].

Dataset Structure

28 countries, 4 rate types, 8 non-EUR currencies tracked:

{
  "version": "2026-02-25",
  "source": "European Commission TEDB",
  "rates": {
    "FI": {
      "country": "Finland",
      "currency": "EUR",
      "standard": 25.5,
      "reduced": [10.0, 13.5],
      "super_reduced": null,
      "parking": null
    },
    "LU": {
      "country": "Luxembourg",
      "currency": "EUR",
      "standard": 17.0,
      "reduced": [8.0, 14.0],
      "super_reduced": 3.0,
      "parking": 14.0
    }
  }
}

Historical rate changes are not stored in the file — they're preserved automatically in git history. git log -- data/eu-vat-rates-data.json gives a complete audit trail of every EU VAT rate change since the project launched.

5 Packages, 5 Ecosystems

All five packages expose the same logical API — getRate, getStandardRate, isEUMember, dataVersion — adapted to each language's idioms.

JavaScript / TypeScript — npm install eu-vat-rates-data · GitHub

npm

Full TypeScript types. CountryCode is a union literal of all 28 codes. getRate is overloaded: pass a CountryCode and get VatRate (never undefined); pass a plain string and get VatRate | undefined. isEUMember is a type guard that narrows string to CountryCode.

import { getRate, isEUMember, dataVersion } from "eu-vat-rates-data";
 
if (isEUMember(userInput)) {
  const rate = getRate(userInput); // VatRate — never undefined here
  console.log(`${rate.country}: ${rate.standard}%`);
}
console.log(dataVersion); // "2026-02-25"

Published as dual CJS + ESM with .d.ts declarations.

Python — pip install eu-vat-rates-data · GitHub

PyPI

TypedDict with Optional annotations. Data loaded via importlib.resources.files() — the modern Python approach that works correctly inside wheels and zip archives.

from eu_vat_rates_data import get_rate, is_eu_member
 
rate = get_rate("FI")
# VatRate(country='Finland', currency='EUR', standard=25.5, ...)
 
if is_eu_member("DE"):
    print(get_rate("DE")["standard"])  # 19.0

PHP — composer require vatnode/eu-vat-rates-data · GitHub

Packagist

final class EuVatRates with all-static methods and lazy loading — JSON is read once on first access. PHPDoc array shape annotations for IDE support.

use VATNode\EuVatRates\EuVatRates;
 
$rate = EuVatRates::getRate('FI');       // ['standard' => 25.5, 'reduced' => [...]]
EuVatRates::getStandardRate('DE');       // 19.0
EuVatRates::isEuMember('US');           // false

Go — go get github.com/vatnode/eu-vat-rates-data-go · GitHub

Go

JSON embedded via //go:embed — the binary contains the dataset, no runtime file I/O. Nullable fields use pointer types (*float64). Parsing runs in init().

import euvatrates "github.com/vatnode/eu-vat-rates-data-go"
 
rate, ok := euvatrates.GetRate("FI")
// rate.Standard == 25.5, rate.Country == "Finland"
 
standard, _ := euvatrates.GetStandardRate("DE") // 19.0
euvatrates.IsEUMember("US")                      // false

Ruby — gem install eu_vat_rates_data · GitHub

RubyGems

Module EuVatRatesData with lazy memoization (@dataset ||= JSON.parse(...)). Safe navigation operator for nullable fields.

require "eu_vat_rates_data"
 
rate = EuVatRatesData.get_rate("FI")
# => {"country"=>"Finland", "standard"=>25.5, "reduced"=>[10.0, 13.5], ...}
 
EuVatRatesData.get_standard_rate("DE") # => 19.0
EuVatRatesData.eu_member?("US")        # => false

Automation Architecture

The JS repository is the single source of truth. Its GitHub Actions workflow runs at 07:00 UTC daily:

  1. Fetches rates from EC TEDB SOAP API
  2. Compares with existing eu-vat-rates-data.json (rates only, not version date)
  3. If rates changed: bumps version (2026.M.D, with counter suffix if that version exists), rebuilds, publishes to npm, commits + pushes
  4. If unchanged: updates version date in JSON but skips publish and tagging

All other language repos (Python, PHP, Go, Ruby) run at 08:00 UTC — one hour later — and pull the JSON directly from the JS repo via curl. This guarantees the source file is already committed before dependent workflows start.

Iurii RoguliaAvailable

Need something similar?

I build custom solutions — from APIs to full products. Let's talk about your project.

View all projects

Related projects

n8n Community Node — EU VAT Validation in Workflows
n8n Community Node — EU VAT Validation in Workflows
August 6, 2026
n8n Community Node — EU VAT Validation in Workflows

Community node that puts VIES VAT validation and EU rate lookups inside n8n, so an onboarding or invoicing workflow can decide on a real VAT number instead of

Stack

TypeScriptNode.jsn8n

Services

npmGitHub Actionsvatnode API

Topics

Workflow AutomationNo-CodeIntegrationsDeveloper ToolsOpen SourceTax/VAT
EU VAT Rates for WooCommerce — WordPress Plugin
EU VAT Rates for WooCommerce — WordPress Plugin
August 5, 2026
EU VAT Rates for WooCommerce — WordPress Plugin

WordPress plugin that keeps WooCommerce EU tax rates current from the European Commission and applies the B2B reverse charge from a VIES-verified VAT number at

Stack

PHPWordPressWooCommerceJavaScript

Services

WordPress.orgEuropean Commission TEDBvatnode API

Topics

WordPress PluginE-commerceB2BTax/VATCheckoutOpen SourceAutomation

Related posts

Publishing One Package to Five Registries with GitHub Actions
March 27, 2026· 10 min
Publishing One Package to Five Registries with GitHub Actions

How to publish one dataset to npm, PyPI, Go Module, RubyGems, and Packagist automatically with GitHub Actions — architecture, versioning, and per-ecosystem

Stack

TypeScriptPythonPHPGoRuby

Services

GitHub ActionsnpmPyPIPackagistRubyGems

Topics

Open SourceTax/VATAutomation
IndexNow in Next.js: Instant Indexing After Every Deploy
April 14, 2026· 18 min
IndexNow in Next.js: Instant Indexing After Every Deploy

IndexNow implementation guide for Next.js: key generation, TypeScript client with retry logic, GitHub Actions workflow, and pitfalls that break submissions

Stack

TypeScriptNode.js

Services

GitHub Actions

Topics

SEOAutomationPerformanceArchitecture