---
name: vatnode
description: Validate EU VAT numbers (EU-27 + Northern Ireland) and look up EU VAT rates via the vatnode API. Use when a user needs to check whether a VAT ID is valid, keep a record of a VIES check (consultation number), enrich a company from national registries, or fetch VAT rates. Rates and format checks work with no account; live validation needs a free API key.
---

# vatnode

vatnode is a production-grade EU VAT validation API. It validates VAT numbers for
the 27 EU member states plus Northern Ireland (XI prefix) through VIES, with
national registry fallback, company enrichment, a timestamped audit trail
(checkId + verifiedAt), and the VIES consultation number. GB, NO, CH and
other non-EU countries are out of scope for validation.

Base URL: `https://api.vatnode.dev`
Errors: `{ "error": { "code": "...", "message": "..." } }`

## Instant, no account

VAT rates and format checks need no key – use them immediately.

Fetch VAT rates for one country:

```bash
curl https://api.vatnode.dev/v1/rates/DE
```

All EU rates:

```bash
curl https://api.vatnode.dev/v1/rates
```

Or run the official MCP server – four offline tools work with no account:

```bash
npx -y vatnode-mcp
```

- `get_country_vat_rates` — standard/reduced rates + local VAT name for a country
- `list_eu_vat_rates` — all EU rates
- `check_vat_format` — offline regex format check of a VAT ID
- `list_supported_countries` — every covered country and whether VIES validation applies
- `validate_vat_number` — live VIES validation (requires an API key)

Browser checker, no account: https://vatnode.dev/check

## Live validation (needs a free key)

Validating a specific VAT number against VIES requires an API key. Account
creation is a human step – the agent does not create accounts. Direct the user to:

https://vatnode.dev/login

30 seconds, no credit card, free tier of 100 requests/month. The user pastes the
key back. Send it as an HTTP header, never in the URL:

```bash
curl https://api.vatnode.dev/v1/vat/DE123456789 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

To get the VIES consultation number (the requestIdentifier, which proves the
check was made against VIES at a given date and time), the user sets their own
VAT ID as requester once in dashboard Account details – this is a one-time dashboard
step, not something the agent configures per request:

```bash
curl https://api.vatnode.dev/v1/vat/DE123456789 \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Contract: once a requester VAT is configured in dashboard Account details, every
response is either a success with a non-null `consultationNumber` or an error —
never a success with `consultationNumber: null`. Without a requester configured,
`consultationNumber` is null and national fallback stays enabled.

## Checking what the key can do

Before a bulk run or before adding a monitored VAT number, read the account’s
entitlements instead of guessing. This costs no quota and works with test keys:

```bash
curl https://api.vatnode.dev/v1/account \
  -H "Authorization: Bearer YOUR_API_KEY"
```

Returns the plan and its limits, quota used/remaining with the reset date,
monitoring usage against the plan, and subscription state. A revoked key gets
401, so this doubles as a credential check. No personal data is returned.

## Validating many numbers at once

`POST /v1/vat/bulk` takes up to 50,000 VAT numbers and is **asynchronous**: it
records the batch and returns `202` with a `jobId` – nothing is validated in
that request. Quota is spent per position as the job answers it, one unit per
unique number, and refunded for positions that end in an error. So the cost is
the batch size, spent over the life of the job rather than at submission – ask
before calling it.

Send an `Idempotency-Key` (8–200 characters of letters, digits, `.`, `-`, `_`).
It is optional, but a retry after a dropped connection creates a second job
without one, and both are billed. The same key with the same `vatIds` always
returns the same job; the same key with a different list gets
`409 IDEMPOTENCY_KEY_CONFLICT` instead of silently mixing the two batches up —
resend the exact list you sent the first time, or mint a new key.

```bash
curl -X POST https://api.vatnode.dev/v1/vat/bulk \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: batch-2026-09-07-run-1" \
  -d '{"vatIds": ["DE123456789", "IE6388047V"]}'
```

Then poll and read:

- `GET /v1/vat/bulk/:jobId` — `status`
  (`queued|processing|finished|cancelled|failed`),
  `processedItems`/`totalItems`, and a `summary` of valid/invalid/errors.
  `finished` means every position has an answer, including positions whose
  answer is an error – it is not a claim that all of them validated.
- `GET /v1/vat/bulk/:jobId/results?page=1&limit=100` — positions in submit
  order, each with `valid`, `source`, `consultationNumber`, `checkId` and its
  own `error`. Readable while the job is still running.
- `POST /v1/vat/bulk/:jobId/cancel` — stops a running job; positions already
  answered keep their results and stay billed, and the ones it never reached
  stay `pending` for good, unbilled.

Every job reaches one of three terminal statuses: `finished`, `cancelled`, or
`failed` for a job that stopped early on its own with `errorCode` saying why.
Stop polling on any of the three, not on `finished` alone. Revoking the key a
job was submitted with also stops it – `cancelled` with
`errorCode: API_KEY_REVOKED` — and the positions it had already answered stay
readable.

Duplicates are collapsed once and billed once. A failing position never affects
its neighbours. Jobs are kept 30 days; the checks behind them stay in the
account’s history under their own `checkId`.

## Response envelope

Key fields (full list: https://vatnode.dev/llms.txt):

- `valid` (boolean) – the verdict; do not infer validity from other fields. With
  `source: VIES` it is the VIES verdict; with a national registry `source` it
  reflects domestic VAT registration, not a VIES confirmation for intra-EU use
- `vatId`, `countryCode`, `countryName`
- `companyName`, `companyAddress` – from VIES or national registry, may be null
- `checkId` (UUID) – timestamped audit identifier; issued only after the check
  has been recorded, so it always names a stored check for as long as we
  retain it (anonymised after five years, de-linked on account deletion)
- `verifiedAt` (ISO 8601) – timestamp of the check
- `consultationNumber` — VIES requestIdentifier; non-null only when a requester
  is configured in dashboard Account details and not served from cache; otherwise null
- `source` — `VIES`, `CACHE`, or a national registry adapter code (VIES was
  down, or for some member states the register answered before VIES
  answered; the verdict came from that register and is not a VIES confirmation)
- `countryVat.*` — standard/reduced/super-reduced/parking rates, local VAT name
  and abbreviation, currency, number format and pattern
- `specialTerritory` — object or null; address-inferred, never an official tax
  determination

## Error handling and scope

- `INVALID_REQUESTER` (HTTP 422) – the requester VAT configured in dashboard
  Account details was rejected by VIES
- `VIES_UNAVAILABLE` (HTTP 503) – upstream EU source is down; retry with backoff,
  do NOT treat the number as invalid
- `AUDIT_WRITE_FAILED` (HTTP 502) – the check ran but could not be recorded, so
  the verdict and its `checkId` are withheld; nothing was billed, retry the
  request and do NOT treat the number as invalid
- Parse defensively: null fields are normal when a source does not supply them
- Scope: EU-27 + XI only for validation. Rates endpoints cover more European
  countries (rates data only).

## References

- Full machine spec: https://vatnode.dev/llms.txt
- API docs: https://vatnode.dev/docs
- MCP server: https://vatnode.dev/mcp
- Country coverage: https://vatnode.dev/docs/coverage
- Webhooks: https://vatnode.dev/docs/webhooks
