eu-vat-rates-data 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,157 @@
1
+ # eu-vat-rates-data
2
+
3
+ [![npm version](https://img.shields.io/npm/v/eu-vat-rates-data)](https://www.npmjs.com/package/eu-vat-rates-data)
4
+ [![Last updated](https://img.shields.io/github/last-commit/rogulia/vatnode?path=packages%2Feu-vat-rates%2Fdata%2Feu-vat-rates.json&label=last%20updated)](https://github.com/rogulia/vatnode/commits/main/packages/eu-vat-rates/data/eu-vat-rates.json)
5
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
6
+
7
+ Auto-updated VAT rates for all **27 EU member states** plus the **United Kingdom**, sourced directly from the [European Commission TEDB](https://taxation-customs.ec.europa.eu/tedb/vatRates.html) SOAP web service.
8
+
9
+ - Standard, reduced, super-reduced, and parking rates
10
+ - TypeScript types included — works in Node.js and the browser
11
+ - JSON file committed to git — full rate-change history via `git log`
12
+ - Refreshed every Monday via GitHub Actions
13
+
14
+ ---
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ npm install eu-vat-rates-data
20
+ # or
21
+ yarn add eu-vat-rates-data
22
+ # or
23
+ pnpm add eu-vat-rates-data
24
+ ```
25
+
26
+ ---
27
+
28
+ ## Usage
29
+
30
+ ### TypeScript / ESM
31
+
32
+ ```ts
33
+ import { getRate, getStandardRate, getAllRates, isEUMember, dataVersion } from 'eu-vat-rates-data'
34
+
35
+ // Full rate object for a country
36
+ const fi = getRate('FI')
37
+ // {
38
+ // country: 'Finland',
39
+ // currency: 'EUR',
40
+ // standard: 25.5,
41
+ // reduced: [10, 13.5],
42
+ // super_reduced: null,
43
+ // parking: null
44
+ // }
45
+
46
+ // Just the standard rate
47
+ getStandardRate('DE') // → 19
48
+
49
+ // Type guard
50
+ if (isEUMember(userInput)) {
51
+ const rate = getRate(userInput) // type: VatRate (never undefined)
52
+ }
53
+
54
+ // All 28 countries at once
55
+ const all = getAllRates()
56
+ Object.entries(all).forEach(([code, rate]) => {
57
+ console.log(`${code}: ${rate.standard}%`)
58
+ })
59
+
60
+ // When were these rates last fetched?
61
+ console.log(dataVersion) // e.g. "2026-02-24"
62
+ ```
63
+
64
+ ### CommonJS
65
+
66
+ ```js
67
+ const { getRate, isEUMember } = require('eu-vat-rates-data')
68
+
69
+ console.log(getRate('FR').standard) // 20
70
+ ```
71
+
72
+ ### Direct JSON (no npm)
73
+
74
+ The raw dataset is always available via jsDelivr CDN:
75
+
76
+ ```
77
+ https://cdn.jsdelivr.net/npm/eu-vat-rates-data/data/eu-vat-rates.json
78
+ ```
79
+
80
+ ```js
81
+ const res = await fetch('https://cdn.jsdelivr.net/npm/eu-vat-rates-data/data/eu-vat-rates.json')
82
+ const { rates } = await res.json()
83
+ console.log(rates.DE.standard) // 19
84
+ ```
85
+
86
+ ---
87
+
88
+ ## Data structure
89
+
90
+ ```ts
91
+ interface VatRate {
92
+ country: string // "Finland"
93
+ currency: string // "EUR" (or "DKK", "GBP", …)
94
+ standard: number // 25.5
95
+ reduced: number[] // [10, 13.5] — sorted ascending
96
+ super_reduced: number | null // null when not applicable
97
+ parking: number | null // null when not applicable
98
+ }
99
+ ```
100
+
101
+ `reduced` may contain rates for special territories (e.g. French DOM departments, Azores/Madeira for Portugal, Canary Islands for Spain). All values come verbatim from EC TEDB.
102
+
103
+ ### Country codes
104
+
105
+ Standard ISO 3166-1 alpha-2, with one EU convention: Greece is `GR` (TEDB internally uses `EL`, which this package normalises).
106
+
107
+ ### Example JSON entry
108
+
109
+ ```json
110
+ {
111
+ "version": "2026-02-24",
112
+ "source": "European Commission TEDB",
113
+ "url": "https://taxation-customs.ec.europa.eu/tedb/vatRates.html",
114
+ "rates": {
115
+ "FI": {
116
+ "country": "Finland",
117
+ "currency": "EUR",
118
+ "standard": 25.5,
119
+ "reduced": [10, 13.5],
120
+ "super_reduced": null,
121
+ "parking": null
122
+ }
123
+ }
124
+ }
125
+ ```
126
+
127
+ ---
128
+
129
+ ## Data source & update frequency
130
+
131
+ Rates are fetched from the **European Commission Taxes in Europe Database (TEDB)** via its official SOAP web service:
132
+
133
+ - WSDL: `https://ec.europa.eu/taxation_customs/tedb/ws/VatRetrievalService.wsdl`
134
+ - Refreshed: **every Monday at 06:00 UTC** (GitHub Actions cron)
135
+ - History: every update is a git commit, so `git log -- packages/eu-vat-rates/data/eu-vat-rates.json` gives a full audit trail of VAT changes across the EU
136
+
137
+ To manually trigger a refresh (requires repo write access):
138
+
139
+ ```bash
140
+ python3 packages/eu-vat-rates/scripts/update.py
141
+ # or dry-run to preview changes without writing:
142
+ python3 packages/eu-vat-rates/scripts/update.py --dry-run
143
+ ```
144
+
145
+ ---
146
+
147
+ ## Covered countries
148
+
149
+ EU-27 member states + United Kingdom (28 countries total):
150
+
151
+ AT BE BG CY CZ DE DK EE ES FI FR GB GR HR HU IE IT LT LU LV MT NL PL PT RO SE SI SK
152
+
153
+ ---
154
+
155
+ ## License
156
+
157
+ MIT
@@ -0,0 +1,313 @@
1
+ {
2
+ "version": "2026-02-24",
3
+ "source": "European Commission TEDB",
4
+ "url": "https://taxation-customs.ec.europa.eu/tedb/vatRates.html",
5
+ "rates": {
6
+ "AT": {
7
+ "country": "Austria",
8
+ "currency": "EUR",
9
+ "standard": 20.0,
10
+ "reduced": [
11
+ 10.0,
12
+ 13.0,
13
+ 19.0
14
+ ],
15
+ "super_reduced": null,
16
+ "parking": null
17
+ },
18
+ "BE": {
19
+ "country": "Belgium",
20
+ "currency": "EUR",
21
+ "standard": 21.0,
22
+ "reduced": [
23
+ 6.0,
24
+ 12.0
25
+ ],
26
+ "super_reduced": null,
27
+ "parking": 12.0
28
+ },
29
+ "BG": {
30
+ "country": "Bulgaria",
31
+ "currency": "BGN",
32
+ "standard": 20.0,
33
+ "reduced": [
34
+ 9.0
35
+ ],
36
+ "super_reduced": null,
37
+ "parking": null
38
+ },
39
+ "CY": {
40
+ "country": "Cyprus",
41
+ "currency": "EUR",
42
+ "standard": 19.0,
43
+ "reduced": [
44
+ 5.0,
45
+ 9.0
46
+ ],
47
+ "super_reduced": 3.0,
48
+ "parking": null
49
+ },
50
+ "CZ": {
51
+ "country": "Czech Republic",
52
+ "currency": "CZK",
53
+ "standard": 21.0,
54
+ "reduced": [
55
+ 12.0
56
+ ],
57
+ "super_reduced": null,
58
+ "parking": null
59
+ },
60
+ "DE": {
61
+ "country": "Germany",
62
+ "currency": "EUR",
63
+ "standard": 19.0,
64
+ "reduced": [
65
+ 7.0
66
+ ],
67
+ "super_reduced": null,
68
+ "parking": null
69
+ },
70
+ "DK": {
71
+ "country": "Denmark",
72
+ "currency": "DKK",
73
+ "standard": 25.0,
74
+ "reduced": [],
75
+ "super_reduced": null,
76
+ "parking": null
77
+ },
78
+ "EE": {
79
+ "country": "Estonia",
80
+ "currency": "EUR",
81
+ "standard": 24.0,
82
+ "reduced": [
83
+ 9.0,
84
+ 13.0
85
+ ],
86
+ "super_reduced": null,
87
+ "parking": null
88
+ },
89
+ "ES": {
90
+ "country": "Spain",
91
+ "currency": "EUR",
92
+ "standard": 21.0,
93
+ "reduced": [
94
+ 10.0
95
+ ],
96
+ "super_reduced": 4.0,
97
+ "parking": null
98
+ },
99
+ "FI": {
100
+ "country": "Finland",
101
+ "currency": "EUR",
102
+ "standard": 25.5,
103
+ "reduced": [
104
+ 10.0,
105
+ 13.5
106
+ ],
107
+ "super_reduced": null,
108
+ "parking": null
109
+ },
110
+ "FR": {
111
+ "country": "France",
112
+ "currency": "EUR",
113
+ "standard": 20.0,
114
+ "reduced": [
115
+ 0.9,
116
+ 1.05,
117
+ 5.5,
118
+ 8.5,
119
+ 10.0,
120
+ 13.0
121
+ ],
122
+ "super_reduced": 2.1,
123
+ "parking": null
124
+ },
125
+ "GB": {
126
+ "country": "United Kingdom",
127
+ "currency": "GBP",
128
+ "standard": 20.0,
129
+ "reduced": [
130
+ 5.0
131
+ ],
132
+ "super_reduced": null,
133
+ "parking": null
134
+ },
135
+ "GR": {
136
+ "country": "Greece",
137
+ "currency": "EUR",
138
+ "standard": 24.0,
139
+ "reduced": [
140
+ 6.0,
141
+ 13.0,
142
+ 17.0
143
+ ],
144
+ "super_reduced": 4.0,
145
+ "parking": 13.0
146
+ },
147
+ "HR": {
148
+ "country": "Croatia",
149
+ "currency": "EUR",
150
+ "standard": 25.0,
151
+ "reduced": [
152
+ 5.0,
153
+ 13.0
154
+ ],
155
+ "super_reduced": null,
156
+ "parking": null
157
+ },
158
+ "HU": {
159
+ "country": "Hungary",
160
+ "currency": "HUF",
161
+ "standard": 27.0,
162
+ "reduced": [
163
+ 5.0,
164
+ 18.0
165
+ ],
166
+ "super_reduced": null,
167
+ "parking": null
168
+ },
169
+ "IE": {
170
+ "country": "Ireland",
171
+ "currency": "EUR",
172
+ "standard": 23.0,
173
+ "reduced": [
174
+ 9.0,
175
+ 13.5
176
+ ],
177
+ "super_reduced": null,
178
+ "parking": null
179
+ },
180
+ "IT": {
181
+ "country": "Italy",
182
+ "currency": "EUR",
183
+ "standard": 22.0,
184
+ "reduced": [
185
+ 5.0,
186
+ 10.0
187
+ ],
188
+ "super_reduced": 4.0,
189
+ "parking": null
190
+ },
191
+ "LT": {
192
+ "country": "Lithuania",
193
+ "currency": "EUR",
194
+ "standard": 21.0,
195
+ "reduced": [
196
+ 5.0,
197
+ 12.0
198
+ ],
199
+ "super_reduced": null,
200
+ "parking": null
201
+ },
202
+ "LU": {
203
+ "country": "Luxembourg",
204
+ "currency": "EUR",
205
+ "standard": 17.0,
206
+ "reduced": [
207
+ 8.0,
208
+ 14.0
209
+ ],
210
+ "super_reduced": 3.0,
211
+ "parking": 14.0
212
+ },
213
+ "LV": {
214
+ "country": "Latvia",
215
+ "currency": "EUR",
216
+ "standard": 21.0,
217
+ "reduced": [
218
+ 5.0,
219
+ 12.0
220
+ ],
221
+ "super_reduced": null,
222
+ "parking": null
223
+ },
224
+ "MT": {
225
+ "country": "Malta",
226
+ "currency": "EUR",
227
+ "standard": 18.0,
228
+ "reduced": [
229
+ 5.0,
230
+ 7.0
231
+ ],
232
+ "super_reduced": null,
233
+ "parking": 12.0
234
+ },
235
+ "NL": {
236
+ "country": "Netherlands",
237
+ "currency": "EUR",
238
+ "standard": 21.0,
239
+ "reduced": [
240
+ 9.0
241
+ ],
242
+ "super_reduced": null,
243
+ "parking": null
244
+ },
245
+ "PL": {
246
+ "country": "Poland",
247
+ "currency": "PLN",
248
+ "standard": 23.0,
249
+ "reduced": [
250
+ 5.0,
251
+ 8.0
252
+ ],
253
+ "super_reduced": 8.0,
254
+ "parking": null
255
+ },
256
+ "PT": {
257
+ "country": "Portugal",
258
+ "currency": "EUR",
259
+ "standard": 23.0,
260
+ "reduced": [
261
+ 6.0,
262
+ 13.0,
263
+ 16.0,
264
+ 22.0
265
+ ],
266
+ "super_reduced": 6.0,
267
+ "parking": 13.0
268
+ },
269
+ "RO": {
270
+ "country": "Romania",
271
+ "currency": "RON",
272
+ "standard": 21.0,
273
+ "reduced": [
274
+ 11.0
275
+ ],
276
+ "super_reduced": null,
277
+ "parking": null
278
+ },
279
+ "SE": {
280
+ "country": "Sweden",
281
+ "currency": "SEK",
282
+ "standard": 25.0,
283
+ "reduced": [
284
+ 6.0,
285
+ 12.0
286
+ ],
287
+ "super_reduced": null,
288
+ "parking": null
289
+ },
290
+ "SI": {
291
+ "country": "Slovenia",
292
+ "currency": "EUR",
293
+ "standard": 22.0,
294
+ "reduced": [
295
+ 5.0,
296
+ 9.5
297
+ ],
298
+ "super_reduced": null,
299
+ "parking": null
300
+ },
301
+ "SK": {
302
+ "country": "Slovakia",
303
+ "currency": "EUR",
304
+ "standard": 23.0,
305
+ "reduced": [
306
+ 5.0,
307
+ 19.0
308
+ ],
309
+ "super_reduced": null,
310
+ "parking": null
311
+ }
312
+ }
313
+ }
package/dist/index.cjs ADDED
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ dataVersion: () => dataVersion,
24
+ dataset: () => dataset,
25
+ getAllRates: () => getAllRates,
26
+ getRate: () => getRate,
27
+ getStandardRate: () => getStandardRate,
28
+ isEUMember: () => isEUMember
29
+ });
30
+ module.exports = __toCommonJS(index_exports);
31
+
32
+ // data/eu-vat-rates.json
33
+ var eu_vat_rates_default = {
34
+ version: "2026-02-24",
35
+ source: "European Commission TEDB",
36
+ url: "https://taxation-customs.ec.europa.eu/tedb/vatRates.html",
37
+ rates: {
38
+ AT: {
39
+ country: "Austria",
40
+ currency: "EUR",
41
+ standard: 20,
42
+ reduced: [
43
+ 10,
44
+ 13,
45
+ 19
46
+ ],
47
+ super_reduced: null,
48
+ parking: null
49
+ },
50
+ BE: {
51
+ country: "Belgium",
52
+ currency: "EUR",
53
+ standard: 21,
54
+ reduced: [
55
+ 6,
56
+ 12
57
+ ],
58
+ super_reduced: null,
59
+ parking: 12
60
+ },
61
+ BG: {
62
+ country: "Bulgaria",
63
+ currency: "BGN",
64
+ standard: 20,
65
+ reduced: [
66
+ 9
67
+ ],
68
+ super_reduced: null,
69
+ parking: null
70
+ },
71
+ CY: {
72
+ country: "Cyprus",
73
+ currency: "EUR",
74
+ standard: 19,
75
+ reduced: [
76
+ 5,
77
+ 9
78
+ ],
79
+ super_reduced: 3,
80
+ parking: null
81
+ },
82
+ CZ: {
83
+ country: "Czech Republic",
84
+ currency: "CZK",
85
+ standard: 21,
86
+ reduced: [
87
+ 12
88
+ ],
89
+ super_reduced: null,
90
+ parking: null
91
+ },
92
+ DE: {
93
+ country: "Germany",
94
+ currency: "EUR",
95
+ standard: 19,
96
+ reduced: [
97
+ 7
98
+ ],
99
+ super_reduced: null,
100
+ parking: null
101
+ },
102
+ DK: {
103
+ country: "Denmark",
104
+ currency: "DKK",
105
+ standard: 25,
106
+ reduced: [],
107
+ super_reduced: null,
108
+ parking: null
109
+ },
110
+ EE: {
111
+ country: "Estonia",
112
+ currency: "EUR",
113
+ standard: 24,
114
+ reduced: [
115
+ 9,
116
+ 13
117
+ ],
118
+ super_reduced: null,
119
+ parking: null
120
+ },
121
+ ES: {
122
+ country: "Spain",
123
+ currency: "EUR",
124
+ standard: 21,
125
+ reduced: [
126
+ 10
127
+ ],
128
+ super_reduced: 4,
129
+ parking: null
130
+ },
131
+ FI: {
132
+ country: "Finland",
133
+ currency: "EUR",
134
+ standard: 25.5,
135
+ reduced: [
136
+ 10,
137
+ 13.5
138
+ ],
139
+ super_reduced: null,
140
+ parking: null
141
+ },
142
+ FR: {
143
+ country: "France",
144
+ currency: "EUR",
145
+ standard: 20,
146
+ reduced: [
147
+ 0.9,
148
+ 1.05,
149
+ 5.5,
150
+ 8.5,
151
+ 10,
152
+ 13
153
+ ],
154
+ super_reduced: 2.1,
155
+ parking: null
156
+ },
157
+ GB: {
158
+ country: "United Kingdom",
159
+ currency: "GBP",
160
+ standard: 20,
161
+ reduced: [
162
+ 5
163
+ ],
164
+ super_reduced: null,
165
+ parking: null
166
+ },
167
+ GR: {
168
+ country: "Greece",
169
+ currency: "EUR",
170
+ standard: 24,
171
+ reduced: [
172
+ 6,
173
+ 13,
174
+ 17
175
+ ],
176
+ super_reduced: 4,
177
+ parking: 13
178
+ },
179
+ HR: {
180
+ country: "Croatia",
181
+ currency: "EUR",
182
+ standard: 25,
183
+ reduced: [
184
+ 5,
185
+ 13
186
+ ],
187
+ super_reduced: null,
188
+ parking: null
189
+ },
190
+ HU: {
191
+ country: "Hungary",
192
+ currency: "HUF",
193
+ standard: 27,
194
+ reduced: [
195
+ 5,
196
+ 18
197
+ ],
198
+ super_reduced: null,
199
+ parking: null
200
+ },
201
+ IE: {
202
+ country: "Ireland",
203
+ currency: "EUR",
204
+ standard: 23,
205
+ reduced: [
206
+ 9,
207
+ 13.5
208
+ ],
209
+ super_reduced: null,
210
+ parking: null
211
+ },
212
+ IT: {
213
+ country: "Italy",
214
+ currency: "EUR",
215
+ standard: 22,
216
+ reduced: [
217
+ 5,
218
+ 10
219
+ ],
220
+ super_reduced: 4,
221
+ parking: null
222
+ },
223
+ LT: {
224
+ country: "Lithuania",
225
+ currency: "EUR",
226
+ standard: 21,
227
+ reduced: [
228
+ 5,
229
+ 12
230
+ ],
231
+ super_reduced: null,
232
+ parking: null
233
+ },
234
+ LU: {
235
+ country: "Luxembourg",
236
+ currency: "EUR",
237
+ standard: 17,
238
+ reduced: [
239
+ 8,
240
+ 14
241
+ ],
242
+ super_reduced: 3,
243
+ parking: 14
244
+ },
245
+ LV: {
246
+ country: "Latvia",
247
+ currency: "EUR",
248
+ standard: 21,
249
+ reduced: [
250
+ 5,
251
+ 12
252
+ ],
253
+ super_reduced: null,
254
+ parking: null
255
+ },
256
+ MT: {
257
+ country: "Malta",
258
+ currency: "EUR",
259
+ standard: 18,
260
+ reduced: [
261
+ 5,
262
+ 7
263
+ ],
264
+ super_reduced: null,
265
+ parking: 12
266
+ },
267
+ NL: {
268
+ country: "Netherlands",
269
+ currency: "EUR",
270
+ standard: 21,
271
+ reduced: [
272
+ 9
273
+ ],
274
+ super_reduced: null,
275
+ parking: null
276
+ },
277
+ PL: {
278
+ country: "Poland",
279
+ currency: "PLN",
280
+ standard: 23,
281
+ reduced: [
282
+ 5,
283
+ 8
284
+ ],
285
+ super_reduced: 8,
286
+ parking: null
287
+ },
288
+ PT: {
289
+ country: "Portugal",
290
+ currency: "EUR",
291
+ standard: 23,
292
+ reduced: [
293
+ 6,
294
+ 13,
295
+ 16,
296
+ 22
297
+ ],
298
+ super_reduced: 6,
299
+ parking: 13
300
+ },
301
+ RO: {
302
+ country: "Romania",
303
+ currency: "RON",
304
+ standard: 21,
305
+ reduced: [
306
+ 11
307
+ ],
308
+ super_reduced: null,
309
+ parking: null
310
+ },
311
+ SE: {
312
+ country: "Sweden",
313
+ currency: "SEK",
314
+ standard: 25,
315
+ reduced: [
316
+ 6,
317
+ 12
318
+ ],
319
+ super_reduced: null,
320
+ parking: null
321
+ },
322
+ SI: {
323
+ country: "Slovenia",
324
+ currency: "EUR",
325
+ standard: 22,
326
+ reduced: [
327
+ 5,
328
+ 9.5
329
+ ],
330
+ super_reduced: null,
331
+ parking: null
332
+ },
333
+ SK: {
334
+ country: "Slovakia",
335
+ currency: "EUR",
336
+ standard: 23,
337
+ reduced: [
338
+ 5,
339
+ 19
340
+ ],
341
+ super_reduced: null,
342
+ parking: null
343
+ }
344
+ }
345
+ };
346
+
347
+ // src/index.ts
348
+ var dataset = eu_vat_rates_default;
349
+ function getRate(code) {
350
+ return dataset.rates[code];
351
+ }
352
+ function getStandardRate(code) {
353
+ return dataset.rates[code]?.standard;
354
+ }
355
+ function getAllRates() {
356
+ return dataset.rates;
357
+ }
358
+ function isEUMember(code) {
359
+ return Object.prototype.hasOwnProperty.call(dataset.rates, code);
360
+ }
361
+ var dataVersion = dataset.version;
362
+ // Annotate the CommonJS export names for ESM import in node:
363
+ 0 && (module.exports = {
364
+ dataVersion,
365
+ dataset,
366
+ getAllRates,
367
+ getRate,
368
+ getStandardRate,
369
+ isEUMember
370
+ });
@@ -0,0 +1,100 @@
1
+ /** ISO 3166-1 alpha-2 codes for EU-27 member states plus the United Kingdom. */
2
+ type CountryCode = 'AT' | 'BE' | 'BG' | 'CY' | 'CZ' | 'DE' | 'DK' | 'EE' | 'ES' | 'FI' | 'FR' | 'GB' | 'GR' | 'HR' | 'HU' | 'IE' | 'IT' | 'LT' | 'LU' | 'LV' | 'MT' | 'NL' | 'PL' | 'PT' | 'RO' | 'SE' | 'SI' | 'SK';
3
+ /** VAT rate data for a single country. */
4
+ interface VatRate {
5
+ /** Full country name. */
6
+ country: string;
7
+ /** ISO 4217 currency code (EUR for eurozone members). */
8
+ currency: string;
9
+ /** Standard VAT rate in percent (e.g. 20 for 20%). */
10
+ standard: number;
11
+ /**
12
+ * Reduced VAT rates in percent, sorted ascending.
13
+ * May include rates for special territories (e.g. French DOM, Azores).
14
+ * Empty array when the country applies no reduced rates (e.g. Denmark).
15
+ */
16
+ reduced: number[];
17
+ /**
18
+ * Super-reduced rate in percent, or null when not applicable.
19
+ * Applied to a narrow set of essentials in some member states.
20
+ */
21
+ super_reduced: number | null;
22
+ /**
23
+ * Parking rate in percent, or null when not applicable.
24
+ * Transitional rate for goods taxed at reduced rates before 1991.
25
+ */
26
+ parking: number | null;
27
+ }
28
+ /** Shape of the bundled dataset JSON. */
29
+ interface VatDataset {
30
+ /** ISO 8601 date when the data was last fetched from EC TEDB. */
31
+ version: string;
32
+ /** Human-readable source description. */
33
+ source: string;
34
+ /** URL of the upstream data source. */
35
+ url: string;
36
+ /** Map of country code → VAT rate data. */
37
+ rates: Record<CountryCode, VatRate>;
38
+ }
39
+ declare const dataset: VatDataset;
40
+
41
+ /**
42
+ * Returns the complete VAT rate record for a country, or `undefined` if the
43
+ * country code is not in the dataset.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const fi = getRate('FI')
48
+ * // { country: 'Finland', currency: 'EUR', standard: 25.5, reduced: [10, 13.5], … }
49
+ * ```
50
+ */
51
+ declare function getRate(code: CountryCode): VatRate;
52
+ declare function getRate(code: string): VatRate | undefined;
53
+ /**
54
+ * Returns the standard VAT rate (percent) for a country, or `undefined`.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * getStandardRate('DE') // 19
59
+ * getStandardRate('FI') // 25.5
60
+ * ```
61
+ */
62
+ declare function getStandardRate(code: CountryCode): number;
63
+ declare function getStandardRate(code: string): number | undefined;
64
+ /**
65
+ * Returns all VAT rate records as a plain object keyed by country code.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const all = getAllRates()
70
+ * Object.entries(all).forEach(([code, rate]) => {
71
+ * console.log(`${code}: ${rate.standard}%`)
72
+ * })
73
+ * ```
74
+ */
75
+ declare function getAllRates(): Record<CountryCode, VatRate>;
76
+ /**
77
+ * Returns `true` when the given string is a country code present in the
78
+ * dataset (EU-27 + GB).
79
+ *
80
+ * Acts as a TypeScript type guard narrowing `string` to `CountryCode`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * isEUMember('DE') // true
85
+ * isEUMember('US') // false
86
+ * ```
87
+ */
88
+ declare function isEUMember(code: string): code is CountryCode;
89
+ /**
90
+ * ISO 8601 date string indicating when the bundled data was last refreshed
91
+ * from the European Commission TEDB.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * console.log(`Rates valid as of ${dataVersion}`)
96
+ * ```
97
+ */
98
+ declare const dataVersion: string;
99
+
100
+ export { type CountryCode, type VatDataset, type VatRate, dataVersion, dataset, getAllRates, getRate, getStandardRate, isEUMember };
@@ -0,0 +1,100 @@
1
+ /** ISO 3166-1 alpha-2 codes for EU-27 member states plus the United Kingdom. */
2
+ type CountryCode = 'AT' | 'BE' | 'BG' | 'CY' | 'CZ' | 'DE' | 'DK' | 'EE' | 'ES' | 'FI' | 'FR' | 'GB' | 'GR' | 'HR' | 'HU' | 'IE' | 'IT' | 'LT' | 'LU' | 'LV' | 'MT' | 'NL' | 'PL' | 'PT' | 'RO' | 'SE' | 'SI' | 'SK';
3
+ /** VAT rate data for a single country. */
4
+ interface VatRate {
5
+ /** Full country name. */
6
+ country: string;
7
+ /** ISO 4217 currency code (EUR for eurozone members). */
8
+ currency: string;
9
+ /** Standard VAT rate in percent (e.g. 20 for 20%). */
10
+ standard: number;
11
+ /**
12
+ * Reduced VAT rates in percent, sorted ascending.
13
+ * May include rates for special territories (e.g. French DOM, Azores).
14
+ * Empty array when the country applies no reduced rates (e.g. Denmark).
15
+ */
16
+ reduced: number[];
17
+ /**
18
+ * Super-reduced rate in percent, or null when not applicable.
19
+ * Applied to a narrow set of essentials in some member states.
20
+ */
21
+ super_reduced: number | null;
22
+ /**
23
+ * Parking rate in percent, or null when not applicable.
24
+ * Transitional rate for goods taxed at reduced rates before 1991.
25
+ */
26
+ parking: number | null;
27
+ }
28
+ /** Shape of the bundled dataset JSON. */
29
+ interface VatDataset {
30
+ /** ISO 8601 date when the data was last fetched from EC TEDB. */
31
+ version: string;
32
+ /** Human-readable source description. */
33
+ source: string;
34
+ /** URL of the upstream data source. */
35
+ url: string;
36
+ /** Map of country code → VAT rate data. */
37
+ rates: Record<CountryCode, VatRate>;
38
+ }
39
+ declare const dataset: VatDataset;
40
+
41
+ /**
42
+ * Returns the complete VAT rate record for a country, or `undefined` if the
43
+ * country code is not in the dataset.
44
+ *
45
+ * @example
46
+ * ```ts
47
+ * const fi = getRate('FI')
48
+ * // { country: 'Finland', currency: 'EUR', standard: 25.5, reduced: [10, 13.5], … }
49
+ * ```
50
+ */
51
+ declare function getRate(code: CountryCode): VatRate;
52
+ declare function getRate(code: string): VatRate | undefined;
53
+ /**
54
+ * Returns the standard VAT rate (percent) for a country, or `undefined`.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * getStandardRate('DE') // 19
59
+ * getStandardRate('FI') // 25.5
60
+ * ```
61
+ */
62
+ declare function getStandardRate(code: CountryCode): number;
63
+ declare function getStandardRate(code: string): number | undefined;
64
+ /**
65
+ * Returns all VAT rate records as a plain object keyed by country code.
66
+ *
67
+ * @example
68
+ * ```ts
69
+ * const all = getAllRates()
70
+ * Object.entries(all).forEach(([code, rate]) => {
71
+ * console.log(`${code}: ${rate.standard}%`)
72
+ * })
73
+ * ```
74
+ */
75
+ declare function getAllRates(): Record<CountryCode, VatRate>;
76
+ /**
77
+ * Returns `true` when the given string is a country code present in the
78
+ * dataset (EU-27 + GB).
79
+ *
80
+ * Acts as a TypeScript type guard narrowing `string` to `CountryCode`.
81
+ *
82
+ * @example
83
+ * ```ts
84
+ * isEUMember('DE') // true
85
+ * isEUMember('US') // false
86
+ * ```
87
+ */
88
+ declare function isEUMember(code: string): code is CountryCode;
89
+ /**
90
+ * ISO 8601 date string indicating when the bundled data was last refreshed
91
+ * from the European Commission TEDB.
92
+ *
93
+ * @example
94
+ * ```ts
95
+ * console.log(`Rates valid as of ${dataVersion}`)
96
+ * ```
97
+ */
98
+ declare const dataVersion: string;
99
+
100
+ export { type CountryCode, type VatDataset, type VatRate, dataVersion, dataset, getAllRates, getRate, getStandardRate, isEUMember };
package/dist/index.js ADDED
@@ -0,0 +1,338 @@
1
+ // data/eu-vat-rates.json
2
+ var eu_vat_rates_default = {
3
+ version: "2026-02-24",
4
+ source: "European Commission TEDB",
5
+ url: "https://taxation-customs.ec.europa.eu/tedb/vatRates.html",
6
+ rates: {
7
+ AT: {
8
+ country: "Austria",
9
+ currency: "EUR",
10
+ standard: 20,
11
+ reduced: [
12
+ 10,
13
+ 13,
14
+ 19
15
+ ],
16
+ super_reduced: null,
17
+ parking: null
18
+ },
19
+ BE: {
20
+ country: "Belgium",
21
+ currency: "EUR",
22
+ standard: 21,
23
+ reduced: [
24
+ 6,
25
+ 12
26
+ ],
27
+ super_reduced: null,
28
+ parking: 12
29
+ },
30
+ BG: {
31
+ country: "Bulgaria",
32
+ currency: "BGN",
33
+ standard: 20,
34
+ reduced: [
35
+ 9
36
+ ],
37
+ super_reduced: null,
38
+ parking: null
39
+ },
40
+ CY: {
41
+ country: "Cyprus",
42
+ currency: "EUR",
43
+ standard: 19,
44
+ reduced: [
45
+ 5,
46
+ 9
47
+ ],
48
+ super_reduced: 3,
49
+ parking: null
50
+ },
51
+ CZ: {
52
+ country: "Czech Republic",
53
+ currency: "CZK",
54
+ standard: 21,
55
+ reduced: [
56
+ 12
57
+ ],
58
+ super_reduced: null,
59
+ parking: null
60
+ },
61
+ DE: {
62
+ country: "Germany",
63
+ currency: "EUR",
64
+ standard: 19,
65
+ reduced: [
66
+ 7
67
+ ],
68
+ super_reduced: null,
69
+ parking: null
70
+ },
71
+ DK: {
72
+ country: "Denmark",
73
+ currency: "DKK",
74
+ standard: 25,
75
+ reduced: [],
76
+ super_reduced: null,
77
+ parking: null
78
+ },
79
+ EE: {
80
+ country: "Estonia",
81
+ currency: "EUR",
82
+ standard: 24,
83
+ reduced: [
84
+ 9,
85
+ 13
86
+ ],
87
+ super_reduced: null,
88
+ parking: null
89
+ },
90
+ ES: {
91
+ country: "Spain",
92
+ currency: "EUR",
93
+ standard: 21,
94
+ reduced: [
95
+ 10
96
+ ],
97
+ super_reduced: 4,
98
+ parking: null
99
+ },
100
+ FI: {
101
+ country: "Finland",
102
+ currency: "EUR",
103
+ standard: 25.5,
104
+ reduced: [
105
+ 10,
106
+ 13.5
107
+ ],
108
+ super_reduced: null,
109
+ parking: null
110
+ },
111
+ FR: {
112
+ country: "France",
113
+ currency: "EUR",
114
+ standard: 20,
115
+ reduced: [
116
+ 0.9,
117
+ 1.05,
118
+ 5.5,
119
+ 8.5,
120
+ 10,
121
+ 13
122
+ ],
123
+ super_reduced: 2.1,
124
+ parking: null
125
+ },
126
+ GB: {
127
+ country: "United Kingdom",
128
+ currency: "GBP",
129
+ standard: 20,
130
+ reduced: [
131
+ 5
132
+ ],
133
+ super_reduced: null,
134
+ parking: null
135
+ },
136
+ GR: {
137
+ country: "Greece",
138
+ currency: "EUR",
139
+ standard: 24,
140
+ reduced: [
141
+ 6,
142
+ 13,
143
+ 17
144
+ ],
145
+ super_reduced: 4,
146
+ parking: 13
147
+ },
148
+ HR: {
149
+ country: "Croatia",
150
+ currency: "EUR",
151
+ standard: 25,
152
+ reduced: [
153
+ 5,
154
+ 13
155
+ ],
156
+ super_reduced: null,
157
+ parking: null
158
+ },
159
+ HU: {
160
+ country: "Hungary",
161
+ currency: "HUF",
162
+ standard: 27,
163
+ reduced: [
164
+ 5,
165
+ 18
166
+ ],
167
+ super_reduced: null,
168
+ parking: null
169
+ },
170
+ IE: {
171
+ country: "Ireland",
172
+ currency: "EUR",
173
+ standard: 23,
174
+ reduced: [
175
+ 9,
176
+ 13.5
177
+ ],
178
+ super_reduced: null,
179
+ parking: null
180
+ },
181
+ IT: {
182
+ country: "Italy",
183
+ currency: "EUR",
184
+ standard: 22,
185
+ reduced: [
186
+ 5,
187
+ 10
188
+ ],
189
+ super_reduced: 4,
190
+ parking: null
191
+ },
192
+ LT: {
193
+ country: "Lithuania",
194
+ currency: "EUR",
195
+ standard: 21,
196
+ reduced: [
197
+ 5,
198
+ 12
199
+ ],
200
+ super_reduced: null,
201
+ parking: null
202
+ },
203
+ LU: {
204
+ country: "Luxembourg",
205
+ currency: "EUR",
206
+ standard: 17,
207
+ reduced: [
208
+ 8,
209
+ 14
210
+ ],
211
+ super_reduced: 3,
212
+ parking: 14
213
+ },
214
+ LV: {
215
+ country: "Latvia",
216
+ currency: "EUR",
217
+ standard: 21,
218
+ reduced: [
219
+ 5,
220
+ 12
221
+ ],
222
+ super_reduced: null,
223
+ parking: null
224
+ },
225
+ MT: {
226
+ country: "Malta",
227
+ currency: "EUR",
228
+ standard: 18,
229
+ reduced: [
230
+ 5,
231
+ 7
232
+ ],
233
+ super_reduced: null,
234
+ parking: 12
235
+ },
236
+ NL: {
237
+ country: "Netherlands",
238
+ currency: "EUR",
239
+ standard: 21,
240
+ reduced: [
241
+ 9
242
+ ],
243
+ super_reduced: null,
244
+ parking: null
245
+ },
246
+ PL: {
247
+ country: "Poland",
248
+ currency: "PLN",
249
+ standard: 23,
250
+ reduced: [
251
+ 5,
252
+ 8
253
+ ],
254
+ super_reduced: 8,
255
+ parking: null
256
+ },
257
+ PT: {
258
+ country: "Portugal",
259
+ currency: "EUR",
260
+ standard: 23,
261
+ reduced: [
262
+ 6,
263
+ 13,
264
+ 16,
265
+ 22
266
+ ],
267
+ super_reduced: 6,
268
+ parking: 13
269
+ },
270
+ RO: {
271
+ country: "Romania",
272
+ currency: "RON",
273
+ standard: 21,
274
+ reduced: [
275
+ 11
276
+ ],
277
+ super_reduced: null,
278
+ parking: null
279
+ },
280
+ SE: {
281
+ country: "Sweden",
282
+ currency: "SEK",
283
+ standard: 25,
284
+ reduced: [
285
+ 6,
286
+ 12
287
+ ],
288
+ super_reduced: null,
289
+ parking: null
290
+ },
291
+ SI: {
292
+ country: "Slovenia",
293
+ currency: "EUR",
294
+ standard: 22,
295
+ reduced: [
296
+ 5,
297
+ 9.5
298
+ ],
299
+ super_reduced: null,
300
+ parking: null
301
+ },
302
+ SK: {
303
+ country: "Slovakia",
304
+ currency: "EUR",
305
+ standard: 23,
306
+ reduced: [
307
+ 5,
308
+ 19
309
+ ],
310
+ super_reduced: null,
311
+ parking: null
312
+ }
313
+ }
314
+ };
315
+
316
+ // src/index.ts
317
+ var dataset = eu_vat_rates_default;
318
+ function getRate(code) {
319
+ return dataset.rates[code];
320
+ }
321
+ function getStandardRate(code) {
322
+ return dataset.rates[code]?.standard;
323
+ }
324
+ function getAllRates() {
325
+ return dataset.rates;
326
+ }
327
+ function isEUMember(code) {
328
+ return Object.prototype.hasOwnProperty.call(dataset.rates, code);
329
+ }
330
+ var dataVersion = dataset.version;
331
+ export {
332
+ dataVersion,
333
+ dataset,
334
+ getAllRates,
335
+ getRate,
336
+ getStandardRate,
337
+ isEUMember
338
+ };
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "eu-vat-rates-data",
3
+ "version": "0.1.0",
4
+ "description": "Auto-updated EU VAT rates dataset for all 27 EU member states + UK, sourced from European Commission TEDB",
5
+ "type": "module",
6
+ "main": "./dist/index.cjs",
7
+ "module": "./dist/index.js",
8
+ "types": "./dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "import": {
12
+ "types": "./dist/index.d.ts",
13
+ "default": "./dist/index.js"
14
+ },
15
+ "require": {
16
+ "types": "./dist/index.d.cts",
17
+ "default": "./dist/index.cjs"
18
+ }
19
+ },
20
+ "./data": "./data/eu-vat-rates.json"
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "data",
25
+ "README.md"
26
+ ],
27
+ "scripts": {
28
+ "build": "tsup src/index.ts --format esm,cjs --dts --clean",
29
+ "typecheck": "tsc --noEmit",
30
+ "update": "python3 scripts/update.py"
31
+ },
32
+ "keywords": [
33
+ "vat",
34
+ "eu",
35
+ "european-union",
36
+ "tax",
37
+ "rates",
38
+ "europe"
39
+ ],
40
+ "license": "MIT",
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "https://github.com/rogulia/vatnode.git",
44
+ "directory": "packages/eu-vat-rates"
45
+ },
46
+ "devDependencies": {
47
+ "tsup": "^8.3.0",
48
+ "typescript": "^5.7.0"
49
+ }
50
+ }