eu-vat-rates-data-go
August 23, 2026 · View on GitHub
VAT rates for 45 European countries — EU-27 plus Norway, Switzerland, UK, and more. EU rates sourced from the European Commission TEDB and checked daily. Non-EU rates maintained manually.
Part of VATNode VAT Rates · canonical dataset · methodology · other languages: JavaScript, Python, PHP, Ruby
- Standard, reduced, super-reduced, and parking rates
EUMemberfield on every country —truefor EU-27,falsefor non-EUVATName— official name of the VAT tax in the country's primary official languageVATAbbr— short abbreviation used locally (e.g. "ALV", "MwSt", "TVA")Format— human-readable VAT number format (e.g."ATU + 8 digits") — unique to this packagePattern— regex for VAT number validation + built-inValidateFormat()— free, no API key needed — unique to this package- Zero dependencies — data embedded with
//go:embed - Fully typed — works with Go 1.21+
- EU rates checked daily via GitHub Actions, new version tagged only when rates change
Also available in: JavaScript/TypeScript (npm) · Python (PyPI) · PHP (Packagist) · Ruby (RubyGems)
Need live VIES validation?
This package gives you VAT rates and format checks for free, offline, in your code. It does not call VIES — ValidateFormat() only checks the shape of a VAT number, not whether it actually exists.
For live VIES validation — confirming a VAT ID is real, pulling the registered company name and address, and getting the VIES consultation number as your reference for the check — there's vatnode:
- Live VIES validation, with national-database fallback when VIES is down
- Registered company name, address, registration date
- VIES consultation number for compliance and audit trails
- Webhooks for VAT status changes
- Official MCP server so AI agents (Claude, Cursor, ChatGPT) can validate VAT IDs directly
- Free tier — no credit card needed
curl https://api.vatnode.dev/v1/vat/IE6388047V \
-H "Authorization: Bearer YOUR_API_KEY"
See what the API adds → · Get a free API key
Installation
go get github.com/vatnode/eu-vat-rates-data-go
Usage
package main
import (
"fmt"
euvatrates "github.com/vatnode/eu-vat-rates-data-go"
)
func main() {
// Full rate struct for a country
fi, ok := euvatrates.GetRate("FI")
if ok {
fmt.Printf("%s: %.1f%% (EU member: %v, tax: %s / %s)\n", fi.Country, fi.Standard, fi.EUMember, fi.VATName, fi.VATAbbr)
// Finland: 25.5% (EU member: true, tax: Arvonlisävero / ALV)
}
// Just the standard rate
standard, ok := euvatrates.GetStandardRate("DE")
fmt.Println(standard) // 19
// EU membership check — false for non-EU countries (GB, NO, CH, ...)
if euvatrates.IsEUMember(userInput) {
rate, _ := euvatrates.GetRate(userInput)
_ = rate
}
// Dataset membership check (all 45 countries)
if euvatrates.HasRate(userInput) {
rate, _ := euvatrates.GetRate(userInput)
_ = rate
}
// All 45 countries
all := euvatrates.GetAllRates()
for code, rate := range all {
fmt.Printf("%s: %.1f%%\n", code, rate.Standard)
}
// When were EU rates last fetched?
fmt.Println(euvatrates.DataVersion()) // "2026-03-27"
// VAT number format validation — no API key, no network call
euvatrates.ValidateFormat("ATU12345678") // → true
euvatrates.ValidateFormat("DE123456789") // → true
euvatrates.ValidateFormat("INVALID") // → false
// Access format metadata directly
at, _ := euvatrates.GetRate("AT")
fmt.Println(at.Format) // "ATU + 8 digits"
fmt.Println(at.Pattern) // "^ATU\\d{8}$"
// Flag emoji from a 2-letter country code — no lookup table, computed from regional indicator symbols
fmt.Println(euvatrates.GetFlag("FI")) // "🇫🇮"
fmt.Println(euvatrates.GetFlag("DE")) // "🇩🇪"
fmt.Println(euvatrates.GetFlag("XX")) // "" (empty string for unknown/invalid codes)
}
Example: charging VAT on an invoice
Rates alone do not determine invoice treatment. Resolve place-of-supply, customer status, category, exemptions, and any reverse-charge eligibility in your tax logic first; then use the dataset for the applicable numeric rate.
import (
"math"
euvatrates "github.com/vatnode/eu-vat-rates-data-go"
)
// Money in minor units (cents). Never floats.
type Invoice struct {
VATCents int64
TotalCents int64
ReverseCharge bool
}
func InvoiceTotal(netCents int64, buyerCountry string, reverseChargeEligible bool) Invoice {
if reverseChargeEligible {
return Invoice{VATCents: 0, TotalCents: netCents, ReverseCharge: true}
}
rate, ok := euvatrates.GetStandardRate(buyerCountry)
if !ok {
return Invoice{VATCents: 0, TotalCents: netCents}
}
vatCents := int64(math.Round(float64(netCents) * rate / 100))
return Invoice{VATCents: vatCents, TotalCents: netCents + vatCents}
}
// Domestic sale in Finland — 25.5%
InvoiceTotal(10000, "FI", false)
// → {VATCents: 2550, TotalCents: 12550, ReverseCharge: false}
// Finnish seller, German business buyer — reverse charge
InvoiceTotal(10000, "DE", true)
// → {VATCents: 0, TotalCents: 10000, ReverseCharge: true}
reverseChargeEligible must come from applicable tax logic and evidence.
ValidateFormat() only checks a number's shape; it does not establish registration or eligibility.
Types
type VatRate struct {
Country string `json:"country"`
Currency string `json:"currency"`
EUMember bool `json:"eu_member"`
VATName string `json:"vat_name"`
VATAbbr string `json:"vat_abbr"`
Standard float64 `json:"standard"`
Reduced []float64 `json:"reduced"`
SuperReduced *float64 `json:"super_reduced"`
Parking *float64 `json:"parking"`
Format string `json:"format"` // "ATU + 8 digits"
Pattern string `json:"pattern"` // "^ATU\\d{8}$" — always present for all 45 countries
}
Data source & update frequency
How the daily check works, and what changed when: vatnode.dev/data.
- EU-27 rates: European Commission TEDB, checked against the source daily at 07:00 UTC, updated on any change
- Non-EU rates: maintained manually, updated on official rate changes
- New git tag + pkg.go.dev version published only when rates change
Keeping rates current
Rates are bundled at install time. A new package version is published automatically whenever rates change — but your installed version will not update itself.
Recommended: add Renovate or Dependabot to your repo. They detect new versions and open a PR automatically whenever rates change — no manual update commands needed.
Need real-time accuracy? Fetch the always-current JSON directly:
https://cdn.jsdelivr.net/gh/vatnode/eu-vat-rates-data@main/data/eu-vat-rates-data.json
No package needed — parse it with a single fetch() / http.get() / file_get_contents() call and cache locally.
Covered countries
EU-27 (checked daily against EC TEDB, updated on any change):
AT BE BG CY CZ DE DK EE ES FI FR GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK
Non-EU Europe (manually maintained):
AD AL BA CH GB GE IS LI MC MD ME MK NO RS TR UA XK
Changelog
See CHANGELOG.md.
License
MIT
If you find this useful, a ⭐ on GitHub is appreciated.