# Avatcado Developer Guides (Full Text)
> Complete text of every Avatcado developer guide, for LLM ingestion. For an
> overview of the API itself, see https://www.avatcado.com/llms.txt.
# How to Validate EU VAT Numbers Programmatically
> Learn how to validate EU, UK, Swiss, Norwegian, and Australian VAT/GST numbers using a REST API. Covers VIES, HMRC, code examples in TypeScript and Python, and integration testing with test mode.
Published: 2026-03-17
Updated: 2026-03-17
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/how-to-validate-eu-vat-numbers-programmatically
If you sell B2B in the EU, you need to validate your customer's VAT number before applying the reverse charge mechanism. Getting this wrong means you either charge VAT when you shouldn't (annoying your customer) or skip it when you should (creating a tax liability). This guide covers how to validate VAT numbers programmatically using a REST API, with examples for EU, UK, and other supported regions.
## Why VAT validation matters
Under EU VAT rules, B2B cross-border transactions within the EU can be zero-rated if the buyer provides a valid VAT identification number. This is the "reverse charge" mechanism: the tax obligation shifts from the seller to the buyer.
To apply the reverse charge, you must verify that the buyer's VAT number is valid and active at the time of the transaction. "Valid format" isn't enough. The number must be registered and active with the relevant tax authority. This is also important for fraud prevention: fake VAT numbers are a common vector for VAT fraud.
## How VIES works
The European Commission operates VIES (VAT Information Exchange System), a service that checks VAT numbers against national tax authority databases across all 27 EU member states. When you query VIES with a VAT number, it routes the request to the relevant country's tax authority and returns whether the number is valid.
VIES exposes a SOAP/XML endpoint. A typical request looks like this:
```
POST https://ec.europa.eu/taxation_customs/vies/services/checkVatService
Content-Type: text/xml
DE
123456789
```
The response is XML containing a valid boolean, the company name, and address (when available).
## Pain points of using VIES directly
VIES works, but building a production integration against it is painful:
- SOAP/XML: You need to construct XML payloads and parse XML responses. Most modern stacks don't have great SOAP support.
- Inconsistent availability: VIES depends on each member state's national service, and there's no SLA. In our uptime monitoring, some countries (Italy, Spain) show frequent downtime windows.
- No UK support: Since Brexit, UK VAT numbers aren't in VIES. You need a separate integration with HMRC's API.
- No caching: If VIES is down for a country, your validation fails. You need to build your own caching layer.
- No test mode: You're always hitting the live service, making integration testing unreliable.
## The modern approach: using a REST API
Instead of integrating with VIES directly, you can use a REST API that wraps VIES (and HMRC) and handles caching, error handling, and normalization for you.
Here's a validation request using curl and Avatcado:
```
curl https://api.avatcado.com/v1/validate?vat_number=DE123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
The response is structured JSON:
```
{
"data": {
"valid": true,
"vat_number": "DE123456789",
"country_code": "DE",
"company": {
"name": "ACME GmbH",
"address": "Musterstraße 1, 10115 Berlin"
},
"consultation_number": null,
"requested_at": "2026-03-17T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"request_duration_ms": 1240,
"source_status": "live"
}
}
```
## Code examples
### TypeScript / Node.js
```
const response = await fetch(
"https://api.avatcado.com/v1/validate?vat_number=DE123456789",
{
headers: {
Authorization: "Bearer avat_live_your_api_key",
},
}
);
const { data, meta } = await response.json();
if (data.valid) {
console.log(`Valid VAT: ${data.company.name}`);
if (meta.cached) {
console.log("Result served from cache");
}
} else {
console.log("Invalid VAT number");
}
```
### Python
```
import requests
response = requests.get(
"https://api.avatcado.com/v1/validate",
params={"vat_number": "DE123456789"},
headers={"Authorization": "Bearer avat_live_your_api_key"},
)
result = response.json()
if result["data"]["valid"]:
print(f"Valid VAT: {result['data']['company']['name']}")
else:
print("Invalid VAT number")
```
## UK, Swiss, and Norwegian VAT numbers
The same endpoint handles UK, Swiss (CHE prefix), Liechtenstein (LI prefix), and Norwegian (NO prefix) VAT numbers. Avatcado detects the country prefix and routes the request to the appropriate national tax authority:
```
curl https://api.avatcado.com/v1/validate?vat_number=GB123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
The response format is identical. You don't need conditional logic based on the country.
## Integration testing with test mode
Use a test-mode API key (prefixed avat_test_) with magic VAT numbers to simulate different scenarios without hitting VIES or HMRC:
```
# Always returns valid
curl https://api.avatcado.com/v1/validate?vat_number=DE111111111 \
-H "Authorization: Bearer avat_test_your_test_key"
# Always returns invalid
curl https://api.avatcado.com/v1/validate?vat_number=DE000000000 \
-H "Authorization: Bearer avat_test_your_test_key"
```
This makes your CI/CD pipeline fast and deterministic. See the Avatcado documentation for the full list of magic numbers and error simulations.
## Get started
Avatcado offers a free tier with 500 validations per month, no credit card required. The API, response format, and caching work the same across all plans. The same endpoint also validates Swiss (CHE), Liechtenstein, Norwegian (MVA), and Australian (ABN) numbers.
Start validating for free →
## FAQs
Q: What is a VAT identification number?
A: A VAT identification number is a unique identifier assigned to businesses registered for Value Added Tax in the EU or UK. It consists of a two-letter country prefix followed by digits (and sometimes letters). You need to validate these numbers to determine the correct tax treatment for B2B transactions.
Q: Can I validate UK VAT numbers with VIES?
A: No. Since Brexit, UK VAT numbers (GB prefix) are no longer in VIES. You need to use HMRC's separate API for UK validation. Avatcado handles all supported countries through a single endpoint, routing to VIES, HMRC, or the relevant national registry automatically based on the country prefix.
Q: How long does a VAT validation take?
A: In our monitoring, a live VIES lookup typically takes 500ms to 3 seconds depending on the member state, and HMRC lookups average around 1 second. Avatcado's 25-day cache means repeat lookups for the same number return from cache in milliseconds, without an upstream round trip.
Q: Do I need to validate VAT numbers for B2C sales?
A: No. VAT validation is only relevant for B2B transactions where you need to determine whether the reverse charge mechanism applies. For B2C sales, you charge VAT at the applicable rate regardless of the buyer's location.
## Sources
- VIES on the Web (European Commission): https://ec.europa.eu/taxation_customs/vies/
- VIES checkVatService WSDL (European Commission): https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
- Check a UK VAT number API (HMRC): https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api
- Council Directive (EU) 2018/1910 (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2018/1910/oj
---
# VIES API Guide: EU VAT Validation Explained
> A developer's guide to the VIES SOAP API: how it works, its limitations, common error scenarios, and how to use a modern REST wrapper instead.
Published: 2026-03-17
Updated: 2026-03-17
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vies-api-guide
VIES (VAT Information Exchange System) is the European Commission's service for validating VAT identification numbers across EU member states. If you're building anything that handles B2B transactions in Europe, you'll likely need to interact with it, directly or through a wrapper. This guide covers how VIES works, where it falls short, and how to work around its limitations.
## What is VIES?
VIES is operated by the European Commission's Directorate-General for Taxation and Customs Union (DG TAXUD). It provides a centralized interface to check whether a VAT number is registered and active in any of the 27 EU member states.
When you submit a VAT number to VIES, it doesn't check its own database. Instead, it routes the request to the national tax authority of the relevant member state (e.g., Germany's BZSt, France's DGFiP, Italy's Agenzia delle Entrate). Each country maintains its own database and service. VIES is the routing layer.
This architecture is important to understand because it means VIES availability depends on 27 independent national services, not one central system.
## How the VIES SOAP API works
VIES exposes a SOAP endpoint at https://ec.europa.eu/taxation_customs/vies/services/checkVatService. The Commission also runs an official REST API (POST https://ec.europa.eu/taxation_customs/vies/rest-api/check-vat-number), which is what Avatcado itself calls. The primary SOAP operation is checkVat, which takes a country code and VAT number:
```
FR
82542065479
FR
82542065479
2026-03-17+01:00
true
EXAMPLE SAS
1 RUE DE RIVOLI 75001 PARIS
```
There's also a checkVatApprox operation that performs a fuzzy match against a trader's name and address, but availability varies by country and results are inconsistent.
## VIES limitations developers hit in practice
### Per-country downtime
Because VIES routes to national services, individual countries can go offline independently. The European Commission publishes a member state availability schedule, but unplanned outages are common. In our uptime monitoring, Italy and Spain have historically been the least reliable. When a country's service is down, VIES returns a SOAP fault. Your code needs to handle this gracefully.
### SOAP complexity
SOAP is verbose and requires XML parsing. Most modern languages have moved away from native SOAP support. In JavaScript/TypeScript, you'll need to either construct XML strings manually or use a library like soap or fast-xml-parser. In Python, zeep works but adds a heavy dependency.
### No batch validation
VIES only supports one VAT number per request. If you need to validate a list of numbers (e.g., during a data migration or bulk invoice run), you need to make sequential requests and handle rate limiting yourself.
### Rate limiting without documentation
VIES applies rate limits, but they're not documented. The limits appear to be per-IP and vary. If you exceed them, you get a SOAP fault with the code MS_MAX_CONCURRENT_REQ. There's no Retry-After header or clear guidance on backoff.
### No UK support
Since the end of the Brexit transition period on January 1, 2021, UK VAT numbers are no longer in VIES. To validate GB-prefixed numbers, you need a separate integration with HMRC's VAT Registered Companies API, which has its own authentication (OAuth 2.0), rate limits, and response format.
## Common VIES error scenarios
Here are the SOAP faults you'll encounter and what they mean:
- INVALID_INPUT: The VAT number format is wrong (e.g., wrong length, invalid country code). Validate format client-side before calling VIES.
- MS_UNAVAILABLE: The member state's service is down. You should cache and retry later.
- MS_MAX_CONCURRENT_REQ: Rate limited. Back off and retry.
- TIMEOUT: The member state didn't respond in time. VIES has an internal timeout; some countries are slower than others.
- SERVICE_UNAVAILABLE: VIES itself is down (not a specific country). Rare but it happens during maintenance windows.
## How Avatcado solves these problems
Avatcado wraps VIES, HMRC, the BFS UID Register, the Bronnoysund Register, and the Australian ABR in a REST API that handles the infrastructure you'd otherwise build yourself:
- REST + JSON: Standard HTTP GET with JSON responses. No XML parsing.
- Built-in caching: 25-day response cache. If VIES is down for a country, cached results are returned with meta.cached: true.
- EU, UK, CH, LI, NO, and AU in one endpoint: Avatcado detects the country prefix and routes to VIES, HMRC, BFS, Bronnoysund, or ABR automatically. One API key, one response format, 32 countries.
- Structured errors: Machine-readable error codes instead of SOAP faults. Every error includes a code, message, and HTTP status.
- Test mode: Use magic VAT numbers with a test API key to simulate valid, invalid, and error responses. No live service calls.
- Rate limit transparency: Clear rate limits per tier, documented, with X-RateLimit-* headers on every response.
A typical request:
```
curl https://api.avatcado.com/v1/validate?vat_number=FR82542065479 \
-H "Authorization: Bearer avat_live_your_api_key"
```
See the full API documentation for response schemas, error codes, and SDK examples.
## Get started
Avatcado's free tier includes 500 validations per month with the same API and caching as paid plans. No credit card required.
Try Avatcado free →
## FAQs
Q: Is VIES free to use?
A: Yes. The VIES SOAP API is free and operated by the European Commission. There are no API keys or registration required. However, there are undocumented rate limits, no SLA, and frequent per-country outages that make it unreliable for production use without a caching layer.
Q: Why is VIES returning false for a valid VAT number?
A: VIES can return false negatives during member state outages or degraded service. In our monitoring, some member states silently return 'invalid' instead of an error when their systems are under load. If you suspect a false negative, retry after a few minutes or use a service like Avatcado that detects and handles silent failures.
Q: Does VIES support UK VAT numbers?
A: No. Since the end of the Brexit transition period (January 1, 2021), UK VAT numbers are no longer in the VIES system. To validate GB-prefixed numbers, you need HMRC's VAT Registered Companies API, which requires separate OAuth 2.0 authentication.
Q: What is a VIES consultation number?
A: A consultation number is a timestamped proof of validation issued by VIES when you provide your own VAT number as the requester. It serves as evidence that you verified a trading partner's VAT number at a specific point in time, which some member states require for audit purposes.
## Sources
- VIES checkVatService WSDL (European Commission): https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
- VIES on the Web: help and availability (European Commission): https://ec.europa.eu/taxation_customs/vies/#/help
- Check a UK VAT number API (HMRC): https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api
- Taxation and Customs Union: United Kingdom (European Commission): https://taxation-customs.ec.europa.eu/united-kingdom_en
---
# VAT Validation API Comparison (2026)
> An honest comparison of VAT validation APIs: pricing, features, DX, and SDK support across Avatcado, VATstack, vatlayer, VATsense, vatapi.com, APIStax, and taxid.dev.
Published: 2026-03-17
Updated: 2026-08-13
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-api-comparison
If you need to validate EU VAT numbers via a REST API, you have several options. They all query the same underlying data sources (VIES for the EU, HMRC for the UK), but differ significantly in developer experience, pricing, reliability, SDK support, and non-EU coverage. This comparison covers Avatcado, VATstack, vatlayer, VATsense, vatapi.com, APIStax, and taxid.dev, verified as of August 2026.
## Comparison table
Feature | Avatcado | VATstack | vatlayer | VATsense | vatapi.com | APIStax | taxid.dev
Free tier | 500/mo | 100/mo | 100/mo | 100/mo | 30-day trial | 100 credits (one-time) | 100/mo
Paid from | €39/mo | $15/mo | $9.99/mo | €5.99/mo | Not published | €4.99/mo | $19/mo
Non-EU countries | AU, CH, LI, NO | AU, NO, SG, CH | No | AU, NO, CH, ZA, BR | No | CH, NO, IS | AU, CH, NO
EU validation | Yes (27 + XI) | Yes | Yes | Yes | Yes | Yes (27) | Yes (27)
UK (HMRC) | Yes | Yes | No | Yes | Yes | Yes | Yes
Batch validation | Yes (up to 50) | Yes (API + CSV) | No | No | No | No | No
Consultation numbers | Yes | Yes | No | Yes | Yes | No | Not published
Built-in caching | Yes (25-day + stale fallback) | Automatic retries | No | 412 on outage, not billed | No | No | Yes (24-hour)
Stale cache fallback | Yes | No | No | No | No | No | Not published
Test mode | Yes (magic numbers) | Yes | No | No | No | No | Not published
Typed SDKs | TypeScript + Python | OpenAPI spec | No | Yes (7 langs) | No | Java, PHP, Quarkus | No
Structured errors | Yes (codes + docs_url) | Partial | Basic | Basic | Basic | Basic | Yes (Stripe-style codes)
Rate limit headers | Yes | Not published | No | No | No | No | Not published
OpenAPI spec | Yes | Yes | No | No | No | Yes | Not published
VAT rates endpoint | Yes (free, unlimited) | Yes | Yes | Yes | Yes | No | Not published
HTTPS on all tiers | Yes | Yes | Yes | Yes | Yes | Yes | Yes
Note on the "Paid from" row: Avatcado's €39/mo is the monthly-billing rate for the Pro tier, shown here on the same monthly basis as every other competitor's price in this table. Avatcado also offers annual billing at €29/mo (€348/year) for the same Pro tier, and €99/mo (€1,188/year) for Business, billed annually. Every other vendor's price above is that vendor's own list price as published; check each vendor's site for whether they also offer an annual discount.
## Avatcado
Avatcado is a VAT validation API built for developers. It validates against all 27 EU member states plus Northern Ireland (via VIES), the UK (via HMRC), Switzerland and Liechtenstein (via BFS UID Register), Norway (via Bronnoysund Register), and Australia (via ABR) through a single endpoint. Every response follows a consistent envelope (data / error + meta) with machine-readable error codes and a docs_url that links to the relevant error documentation.
Avatcado's standout features: a test mode with magic VAT numbers that simulate valid, invalid, and error responses without hitting live services. Built-in 25-day caching with stale fallback when VIES or HMRC is down, so your validation calls never fail due to upstream outages. Batch validation for up to 50 numbers in a single request (Pro and Business tiers). Typed SDKs for TypeScript (@avatcado/node) and Python (avatcado) with a { data, error } pattern and typed error classes. And a free, unlimited VAT rates endpoint covering all 32 supported countries.
The free tier is the most generous in the market at 500 validations per month. Pro (€39/mo, or €29/mo billed annually, 10k validations) and Business (€129/mo, or €99/mo billed annually, 50k validations) tiers unlock batch validation and higher rate limits.
The main limitation is that Avatcado is newer than some alternatives, so it has a smaller community and fewer third-party integrations. It covers EU + UK + CH + LI + NO + AU (32 countries). It doesn't offer EORI validation, invoice generation, or Stripe integration. For webhook-based async validation, see the async validation guide.
```
npm install @avatcado/node
```
```
import Avatcado from '@avatcado/node';
const avatcado = new Avatcado('avat_live_your_api_key');
const { data, error } = await avatcado.vat.validate({
vatNumber: 'DE123456789',
});
if (error) {
console.error(error.code, error.message);
} else {
console.log(data.data.valid, data.data.company?.name);
}
```
```
pip install avatcado
```
```
from avatcado import Avatcado
avatcado = Avatcado("avat_live_your_api_key")
result = avatcado.vat.validate("DE123456789")
print(result.data.valid, result.data.company.name)
```
## VATstack
VATstack is the most feature-rich option. Beyond validation, it offers webhooks (validation.succeeded events), Stripe auto-sync for transaction tracking, VAT OSS and EC Sales List reporting, batch validation via both the API and dashboard CSV upload, and IP geolocation. It supports EU, UK, AU, NO, SG, and CH.
VATstack has a test mode with dedicated test API keys and test VAT numbers, and provides an OpenAPI 3.0 spec on GitHub. The pricing jumps from $15/mo (500 validations) to $150/mo (15k validations) with no tier in between, which can be a pain point for growing teams. The free tier is limited to 100 validations per month.
There are no official typed SDKs, just the OpenAPI spec for client generation. Error responses include messages but not always machine-readable codes. VATstack is a good choice if you need a full VAT compliance platform with webhooks, Stripe sync, and reporting.
For a line-by-line head-to-head with Avatcado, see the VATstack alternatives page.
## vatlayer
vatlayer (by APILayer) is one of the oldest VAT validation APIs. It offers validation, rate lookups, and price calculations. Pricing starts at $9.99/mo, making it one of the cheapest paid options.
The downsides are significant: no UK support (EU only, a major gap post-Brexit), no test mode, no typed SDKs, no caching, and the API uses an access key query parameter instead of bearer token authentication. Error responses are minimal. The website and documentation haven't been updated in a while. vatlayer is the cheapest option for basic EU-only validation where DX isn't a priority.
Weighing a switch? The vatlayer alternatives page compares it with Avatcado line by line.
## VATsense
VATsense offers the broadest non-EU country coverage, supporting AU, NO, CH, ZA, and BR alongside the EU and UK. Avatcado now covers AU, CH, LI, and NO as well, so VATsense's unique advantage is ZA and BR. It has a free tier (100/mo) and five paid tiers from €5.99/mo to €119.99/mo, making it easy to scale without overpaying.
VATsense recently published typed SDKs in 7 languages (Python, Node.js/TypeScript, PHP, Go, Ruby, C#/.NET, Java). It also returns consultation numbers, and it returns 412 on upstream outage; failed lookups are not billed.
The API uses HTTP Basic Auth rather than bearer tokens. There's no test mode, no batch validation, no webhooks, and no OpenAPI spec. If you need broad country coverage at a granular price point, VATsense is worth evaluating.
The VATsense alternatives page has the full head-to-head with Avatcado.
## vatapi.com
vatapi.com focuses on the UK market with HMRC-compliant invoice generation, sequential invoice numbering, multiple trading names per organization, and currency conversion using HMRC and ECB rates. It supports EU (VIES) and UK (HMRC) validation only.
There's no permanent free tier (30-day trial only). Pricing is sold through FastSpring checkout and not published on the site. Documentation is available via Postman collection only. There are no typed SDKs, no test mode, no batch validation, and no OpenAPI spec. vatapi.com is best if you need UK-focused invoice features alongside validation.
The vatapi.com alternatives page compares it with Avatcado in detail.
## APIStax
APIStax is a multi-API platform: VAT verification sits alongside roughly a dozen unrelated APIs (HTML-to-PDF, geocoding, barcode generation, QR codes) on a shared credit pool. If you already need one of those other APIs, bundling VAT validation onto the same credits and dashboard is convenient. If you only need VAT validation, it is a secondary feature on a generalist platform rather than a purpose-built product.
Pricing runs from a one-time 100 free credits at signup through €4.99/mo (500 credits) up to €39.99/mo (10,000 credits), with credits shared across every API on the platform. It supports the EU 27, the UK, Switzerland, Norway, and Iceland (31 countries), and ships SDKs for Java, PHP, and Quarkus plus an OpenAPI spec. There is no webhook support, no batch validation, and no built-in caching layer for VAT lookups, and server errors don't consume credits.
See the APIStax alternatives page for the full comparison with Avatcado.
## taxid.dev
taxid.dev is a newer, single-purpose VAT validation API: a VIES wrapper with per-tier USD pricing and Stripe-style structured error codes (vat_invalid, service_unavailable) instead of raw SOAP faults. Its site states coverage of 31 countries and now names a registry source for each region: VIES for the EU, HMRC for the UK, ABR for Australia, the BFS UID register for Switzerland, and the Bronnoysund register for Norway.
Pricing starts at $0/mo for 100 validations, then $19/mo (1,000), $49/mo (10,000), and $149/mo (100,000), with a Scale plan at 1,000,000/month and custom pricing above 100,000 per month. It offers Redis-backed caching with sub-10ms cached responses and a public playground at taxid.dev/try (shared demo key, no account required), but no documented batch validation, no webhooks, no published SDK packages (only code snippets), and no published OpenAPI spec or rate limits at the time of writing.
## Who should use what
- Best for developers: Avatcado. Typed SDK, magic test numbers, structured errors with docs links, batch validation, stale cache fallback, free VAT rates, 32 countries across 5 regions, and 500/mo free tier.
- Best for full VAT compliance: VATstack. Webhooks, Stripe sync, OSS reporting, batch, geolocation. The most complete platform, at a higher price.
- Cheapest EU-only option: vatlayer. $9.99/mo, but no UK support and dated DX.
- Best country coverage on a budget: VATsense. AU, NO, CH, ZA, BR support, 7 SDKs, granular pricing from €5.99/mo.
- UK invoicing focus: vatapi.com. HMRC-compliant invoicing, currency conversion, but limited developer tooling.
- Already using other APIStax APIs: APIStax. Shared credits across PDF, geocoding, and barcode APIs, plus Java, PHP, and Quarkus SDKs, but VAT is a secondary feature with no batch or caching.
- Cheapest USD entry tier: taxid.dev. $19/mo undercuts most EUR-priced competitors for US-based teams, but it lacks batch validation, webhooks, and published SDKs.
## Try it yourself
The best way to evaluate is to make a few requests. Avatcado's free tier gives you 500 validations per month, enough to build and test a complete integration before committing.
```
import Avatcado from '@avatcado/node';
const avatcado = new Avatcado('avat_test_your_api_key');
const { data, error } = await avatcado.vat.validate({
vatNumber: 'DE123456789',
});
```
```
from avatcado import Avatcado
avatcado = Avatcado("avat_test_your_api_key")
result = avatcado.vat.validate("DE123456789")
```
Or with curl:
```
curl https://api.avatcado.com/v1/validate?vat_number=DE123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
Read the Avatcado documentation for the full API reference, SDK setup, and test mode guide.
Try Avatcado free →
## Sources
Feature and pricing claims for each competitor were verified against the vendor's own public pages, verified 2026-08-13. Re-check before citing, as pricing and feature availability can change without notice.
- VATstack, verified 2026-08-13: vatstack.com/pricing and vatstack.com/docs
- vatlayer, verified 2026-08-13: vatlayer.com and vatlayer.com/product
- VATsense, verified 2026-08-13: vatsense.com and vatsense.com/documentation
- vatapi.com, verified 2026-08-13: vatapi.com
- APIStax, verified 2026-08-13: apistax.io and apistax.io/pricing
- taxid.dev, verified 2026-08-13: taxid.dev and taxid.dev/pricing
## FAQs
Q: Which VAT validation API has the best free tier?
A: Avatcado offers 500 free validations per month, the most generous free tier among VAT validation APIs. VATstack, vatlayer, and VATsense each offer 100 per month. vatapi.com only offers a 30-day trial with no permanent free tier.
Q: Do all VAT APIs use the same data source?
A: Yes, for EU validation. All VAT APIs query the same underlying VIES system operated by the European Commission. The differences are in reliability (caching, retry logic), developer experience (response formats, SDKs, error handling), and additional features like UK support via HMRC.
Q: Can I switch between VAT validation providers easily?
A: It depends on how tightly coupled your code is to the provider's SDK and response format. If you use raw HTTP calls, switching is straightforward since most APIs accept a VAT number and return a valid/invalid result. Avatcado's response envelope and error codes are designed to be predictable, making migration simpler.
## Sources
- Vatstack pricing (Vatstack): https://vatstack.com/pricing
- vatlayer subscription plans (APILayer): https://vatlayer.com/product
- VAT Sense pricing (VAT Sense): https://vatsense.com/pricing
- APIstax pricing (APIstax): https://apistax.io/pricing
- taxid.dev pricing (taxid.dev): https://taxid.dev/pricing
---
# How to Handle VAT Validation in Your SaaS Billing Flow
> Learn how to integrate real-time VAT validation into your SaaS checkout flow. Covers reverse charge logic, Stripe integration, and storing validation proof.
Published: 2026-03-24
Updated: 2026-03-24
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-saas-billing
If you sell B2B SaaS to European businesses, VAT validation is not optional. You need to determine whether to charge VAT or apply the reverse charge on every transaction. Getting this wrong means either overcharging (annoying customers) or undercharging (creating a tax liability for your company).
## Why real-time validation matters
VAT validation must happen in real-time during checkout or signup, not asynchronously or after the fact. You can't block checkout on an async webhook. You can't retroactively fix an invoice that charged VAT when it shouldn't have, or one that skipped VAT when it should have been applied.
The validation result determines the tax treatment of the transaction at the moment it happens. A valid cross-border VAT number means reverse charge (zero-rate). An invalid number, or no number at all, means you charge VAT. That decision has to be made before you generate the invoice, not after.
## The validation flow
Here is the full pattern, step by step. This applies regardless of your stack, billing provider, or framework.
- Collect: Ask for the VAT number in your signup or checkout UI. Make it optional. Not all buyers are VAT-registered, and sole proprietors or consumers won't have one.
- Validate: Call the API to verify the number is registered and active. Show the result to the user in real-time so they can correct typos or provide a different number.
- Decide: If the number is valid and the buyer is in a different country than you (cross-border B2B), apply the reverse charge and zero-rate VAT. If the number is invalid, missing, or the buyer is in your country, charge VAT at the applicable rate.
- Store: Save the validation result (valid/invalid, company name, consultation number if applicable) as proof. You need this for tax audits.
- Invoice: Generate a compliant invoice reflecting the tax treatment you applied. If you zero-rated, the invoice must reference the reverse charge mechanism.
## Code example
Using the @avatcado/node SDK, here is a TypeScript function that determines tax treatment during checkout:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
async function handleCheckout(buyerVatNumber: string | null, sellerCountry: string) {
if (!buyerVatNumber) {
// No VAT number provided, charge VAT
return { chargeVat: true, vatRate: getVatRate(sellerCountry) };
}
const { data, error } = await avatcado.vat.validate({ vatNumber: buyerVatNumber });
if (error) {
// Validation failed (upstream down, invalid format, etc.)
// Conservative approach: charge VAT and let customer dispute
console.error(`VAT validation failed: ${error.code}`);
return { chargeVat: true, vatRate: getVatRate(sellerCountry) };
}
if (data.data.valid && data.data.countryCode !== sellerCountry) {
// Valid VAT, cross-border B2B: apply reverse charge
return {
chargeVat: false,
reverseCharge: true,
validation: {
vat_number: data.data.vatNumber,
company_name: data.data.company?.name,
validated_at: data.data.requestedAt,
},
};
}
// Same country or invalid: charge VAT
return { chargeVat: true, vatRate: getVatRate(data.data.countryCode || sellerCountry) };
}
```
Or with a simple curl request:
```
curl https://api.avatcado.com/v1/validate?vat_number=DE123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
See the Avatcado documentation for the full response schema, error codes, and SDK reference.
## Plugging into your billing tool
### Stripe
When a buyer provides a valid VAT number, set tax_exempt: "reverse" on the Stripe Customer object and store the VAT number in customer metadata. Stripe will then generate zero-rated invoices for that customer. Store the Avatcado validation result (company name, consultation number) in invoice metadata for your audit trail.
If the validation fails or the buyer is in the same country as you, leave tax_exempt as "none" and configure Stripe Tax or manual tax rates for the applicable VAT rate. The key point: Avatcado handles the validation, Stripe handles the invoicing, your code connects the two.
### Paddle and Lemon Squeezy
These are merchant of record platforms that handle tax calculation and remittance for you. They compute the correct VAT automatically. However, you may still want to validate VAT numbers to determine if the buyer qualifies for reverse charge treatment before the transaction reaches the platform. Validating upfront means the platform applies the correct tax treatment from the start, avoiding corrections later.
### Custom billing
If you built your own billing system, you own the entire flow. Avatcado is the validation layer: call the API before generating each invoice, store the result, and use it to determine the tax line items. See the reverse charge guide for the regulatory details on when zero-rating applies.
## Get started
Avatcado's free tier includes 500 validations per month, enough to build and test a complete billing integration. The API and response format are the same across all plans.
Start validating for free →
## FAQs
Q: Do I need to validate VAT numbers if I use Stripe Tax?
A: Stripe Tax calculates and collects the correct tax, but it does not verify that a VAT number is registered and active. You still need to validate the number to determine whether the reverse charge applies. Avatcado handles the validation, Stripe handles the tax calculation.
Q: What happens if VIES is down during checkout?
A: If you call VIES directly, your checkout flow breaks. Avatcado's 25-day cache and stale fallback ensure that validation requests succeed even when upstream services are down, for any number that has been validated before. The response includes meta fields indicating whether the result came from cache.
Q: Should I validate VAT numbers on the frontend or backend?
A: Always validate on the backend. Frontend validation can be bypassed and should only be used for format checking (showing the user immediate feedback). The actual VIES/HMRC lookup must happen server-side before you finalize the tax treatment.
Q: How do I handle a customer who provides an invalid VAT number?
A: Charge VAT at the applicable rate. You cannot apply the reverse charge without a verified, active VAT number. Show the customer a clear message explaining why their number was rejected and let them retry or proceed without the VAT exemption.
## Sources
- Collect customer tax IDs with Checkout (Stripe): https://docs.stripe.com/tax/checkout/tax-ids
- Tax rates: tax exempt and reverse charge (Stripe): https://docs.stripe.com/billing/taxes/tax-rates
- Council Directive (EU) 2018/1910 (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2018/1910/oj
---
# EU VAT Reverse Charge: What Developers Need to Know
> Understand the EU reverse charge mechanism in developer terms. When it applies, how to validate with consultation numbers, and what your code needs to handle.
Published: 2026-03-24
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/eu-vat-reverse-charge-developers-guide
The reverse charge mechanism shifts the obligation to account for VAT from the seller to the buyer. If you sell B2B services across EU borders, this is the rule that lets you zero-rate your invoices instead of charging VAT. Understanding how it works is essential for any developer building billing or checkout flows for European markets.
## What is the reverse charge?
Normally, the seller charges VAT on a transaction and remits it to their tax authority. With reverse charge, the seller charges 0% VAT and the buyer self-assesses the VAT in their own country. The buyer reports the VAT as both output tax and input tax on their return, which typically nets to zero if they're entitled to full deduction.
This avoids the seller having to register for VAT in every EU country where they have customers. Without reverse charge, a German SaaS company selling to businesses in France, Spain, and Italy would need VAT registrations in all three countries.
Reverse charge applies to cross-border B2B supplies where the buyer is an EU VAT-registered business and the seller is not established in the buyer's country. For B2C sales, normal VAT rules apply (which is why you need to know whether the buyer is a business). In code terms: the presence of a valid VAT number is your primary signal for distinguishing B2B from B2C.
## Why VAT validation is legally required
This is not just good practice. Since January 1, 2020, a valid VAT number is a material requirement for applying the 0% rate on intra-Community supplies of goods in the EU (Council Directive 2018/1910). For cross-border B2B services, the buyer's VAT number is what evidences their taxable status and is required for your recapitulative statement. "Material requirement" means it is a condition of the law, not an optional best practice. If you zero-rate a transaction and the buyer's VAT number turns out to be invalid, you are liable for the VAT that should have been charged, plus potential interest and penalties.
The European Commission's VIES system is the official tool for EU verification, and you must be able to prove you validated the buyer's number at the time of the transaction. That means storing the validation result and, where available, the consultation number.
## When does the reverse charge apply?
All four conditions must be met:
- Both parties must be VAT-registered businesses. The buyer needs a valid VAT identification number, and the seller must be VAT-registered in their own country.
- The transaction must be cross-border. The buyer and seller must be established in different EU member states. A sale from a German company to another German company is a domestic transaction, and normal VAT rules apply.
- The supply must be services. For goods, different rules apply depending on the Incoterms and delivery arrangements. For SaaS companies, the "services" condition is almost always met.
- The buyer must provide a valid, active VAT identification number. Not just well-formatted, but actually registered and active in VIES.
If any of these conditions are not met, you charge VAT at the rate of the seller's country (or the buyer's country under certain distance selling rules). This is why validation matters: an invalid VAT number means you cannot apply the reverse charge, and you must charge VAT.
## Who needs to validate?
- EU businesses selling B2B to other EU countries (intra-Community supplies)
- Non-EU businesses (US, Canada, Israel, Australia) selling B2B services to EU customers. See the US SaaS guide for details
- Any business applying the reverse charge on its invoices
If you use a merchant of record (Paddle, Lemon Squeezy), they handle this for you. If you use Stripe, Mollie, Adyen, Braintree, or any standard payment processor, you handle this yourself.
## The decision flow
When a customer enters a VAT number during checkout, here is the logic your billing system should follow:
First, does the buyer have a VAT number? If no, this is a B2C sale (or an unregistered business). Charge VAT.
Second, is the buyer in a different EU country than the seller? If no, this is a domestic sale. Reverse charge does not apply. Charge VAT.
Third, is the VAT number valid and active? Validate it against VIES. If the number is invalid or inactive, you cannot apply the reverse charge. Charge VAT.
Fourth, all conditions are met. Apply the reverse charge. Zero-rate the invoice. Record the validation proof (including the consultation number, if available).
In code, that flow looks like this:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
async function determineVatTreatment(vatNumber: string | null, sellerCountry: string, buyerCountry: string) {
// No VAT number: B2C or unregistered business
if (!vatNumber) {
return { reverseCharge: false, vatRate: getVatRate(buyerCountry) };
}
// Same country: domestic sale, reverse charge does not apply
if (buyerCountry === sellerCountry) {
return { reverseCharge: false, vatRate: getVatRate(sellerCountry) };
}
const { data, error } = await avatcado.vat.validate({ vatNumber });
if (error || !data.data.valid) {
// Invalid or unverifiable: treat as B2C, charge local VAT
return { reverseCharge: false, vatRate: getVatRate(buyerCountry) };
}
// Valid, cross-border, B2B: apply the reverse charge
return {
reverseCharge: true,
vatRate: 0,
companyName: data.data.company?.name,
consultationNumber: data.data.consultationNumber,
};
}
```
## Reverse charge for non-EU sellers
US, Canadian, and Israeli SaaS companies selling to EU businesses can apply the reverse charge too. The EU buyer self-accounts for VAT, and the non-EU seller invoices without VAT.
The seller still needs to validate the buyer's VAT number to confirm they are a real, registered business. Without validation, an individual could claim to be a business to avoid paying VAT, and the seller would end up non-compliant. VIES is freely accessible from outside the EU, so there is no jurisdictional barrier to validating before you invoice.
## Consultation numbers
When you validate a VAT number through VIES and provide your own VAT number as the requester, VIES issues a consultation number. This is a timestamped identifier that proves you verified the buyer's VAT number at the time of the transaction. Some member states require this for audit purposes. Even where it's not strictly required, it's good practice to store it.
With Avatcado, you get a consultation number by passing requester_vat_number as a query parameter:
```
curl "https://api.avatcado.com/v1/validate?vat_number=FR82542065479&requester_vat_number=DE123456789" \
-H "Authorization: Bearer avat_live_your_api_key"
```
The response includes the consultation number alongside the validation result:
```
{
"data": {
"valid": true,
"vat_number": "FR82542065479",
"country_code": "FR",
"company": {
"name": "EXAMPLE SAS",
"address": "1 RUE DE RIVOLI, 75001 PARIS"
},
"consultation_number": "WAPIAAAAA1BBB2",
"requested_at": "2026-03-24T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"request_duration_ms": 890
}
}
```
Or using the TypeScript SDK:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
const { data, error } = await avatcado.vat.validate({
vatNumber: "FR82542065479",
requesterVatNumber: "DE123456789",
});
if (data?.data.valid && data.data.consultationNumber) {
// Store this as proof of verification
await saveValidationProof({
buyer_vat: data.data.vatNumber,
consultation_number: data.data.consultationNumber,
validated_at: data.data.requestedAt,
company_name: data.data.company?.name,
});
}
```
## Your obligations as the seller
When applying the reverse charge, you must:
- Verify the buyer's VAT number is valid and active. Format validation alone is not sufficient. You need to confirm the number is registered in VIES.
- Keep proof of validation. Store the API response, consultation number, and timestamp. You may need this during a tax audit.
- Issue a compliant invoice. The invoice must state "Reverse charge: VAT to be accounted for by the recipient" (or equivalent wording, sometimes citing Article 196 of Directive 2006/112/EC, per local requirements).
- Include both VAT numbers. The seller's and buyer's VAT identification numbers must appear on the invoice.
- Report in your EC Sales List. Cross-border B2B supplies must be reported periodically in the recapitulative statement (Zusammenfassende Meldung in Germany, etat recapitulatif / DES in France).
The specific wording and reporting requirements vary by member state. Consult your accountant for the rules in your jurisdiction.
## Revalidation
VAT numbers can be deactivated, revoked, or changed after you first validate them. For one-time sales, validate at the time of the transaction and you are done. For subscriptions and other recurring billing, revalidate periodically. Monthly or quarterly is common practice, since a buyer's VAT registration can lapse between billing cycles.
Avatcado's 25-day cache means repeat lookups within that window are served from cache and return fast. They still count toward your monthly quota.
## Get started
Avatcado makes it straightforward to build reverse charge logic into your billing flow. The free tier includes 500 validations per month. Consultation numbers are available on all plans.
Read the SaaS billing integration guide for the full checkout flow, or check the API documentation for endpoint details.
Start validating for free →
## FAQs
Q: Is VAT number validation legally required for the reverse charge?
A: Yes. Since January 1, 2020, a valid VAT number is a material requirement for applying the 0% rate on intra-Community supplies in the EU. Without it, you cannot legally zero-rate the transaction, and you become liable for the unpaid VAT if the number turns out to be invalid.
Q: Does the reverse charge apply to UK transactions after Brexit?
A: No. The EU reverse charge mechanism only applies to cross-border B2B transactions within the EU. Since the UK is no longer an EU member state, sales to UK businesses follow different rules. However, you should still validate UK VAT numbers via HMRC for your records.
Q: What is a consultation number and do I need one?
A: A consultation number is a timestamped proof from VIES that you verified a buyer's VAT number. You get one by including your own VAT number in the validation request. While not required in all member states, it is considered best practice for audit purposes.
Q: Can I apply the reverse charge for domestic transactions?
A: No. The reverse charge for services only applies to cross-border B2B transactions within the EU. If you and your customer are in the same country, you charge domestic VAT at the standard rate regardless of whether they have a valid VAT number.
Q: What should my zero-rated invoice include?
A: A reverse charge invoice must include both the seller's and buyer's VAT numbers, a reference to the reverse charge mechanism (e.g., 'Reverse charge: VAT to be accounted for by the recipient'), and the transaction amount without VAT. Specific wording requirements vary by member state.
Q: I'm a US SaaS company. Do I need to validate EU VAT numbers?
A: If you sell B2B to EU customers and apply the reverse charge, which you should to avoid registering for VAT in each EU country, then yes, you need to validate the buyer's VAT number before zero-rating the invoice.
Q: What happens if I don't validate and the number is fake?
A: You are liable for the VAT that should have been charged. Tax authorities can assess you retroactively for the unpaid VAT, plus interest and penalties.
Q: How often should I revalidate a customer's VAT number?
A: For one-time sales, validate at the time of the transaction. For subscriptions, quarterly revalidation is a common practice, since a VAT registration can lapse between billing cycles. Avatcado's cache makes repeat lookups fast.
## Sources
- Council Directive (EU) 2018/1910 (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2018/1910/oj
- Council Directive 2006/112/EC (VAT Directive) (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2006/112/oj
- VAT invoicing rules (European Commission): https://taxation-customs.ec.europa.eu/taxation/vat/vat-businesses/invoicing_en
- VIES on the Web (European Commission): https://ec.europa.eu/taxation_customs/vies/
---
# HMRC VAT Check API: UK VAT Validation Guide
> How to validate UK VAT numbers using the HMRC API. Covers OAuth 2.0 authentication, endpoint structure, common errors, and a simpler alternative.
Published: 2026-03-24
Updated: 2026-03-24
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/hmrc-vat-check-api-guide
Since Brexit, UK VAT numbers (GB prefix) are no longer in the EU's VIES system. To validate UK VAT numbers, you need to integrate with HMRC's "Check a UK VAT Number" API. This guide covers how the HMRC API works, its authentication requirements, common pain points, and how to avoid building the integration from scratch.
## What is the HMRC VAT Check API?
HMRC (HM Revenue and Customs) provides the "Check a UK VAT Number" API, also called the VAT Registered Companies API. It's a REST API (not SOAP like VIES), but it comes with its own complexity: OAuth 2.0 authentication, a sandbox/production split, and an application approval process.
The API validates whether a GB-prefixed VAT number is registered and active, and returns the company name, address, and a processing date. For the UK number format itself, see the UK VAT number format page.
## Authentication: the OAuth 2.0 flow
This is the main pain point. HMRC uses OAuth 2.0 with the client credentials grant for server-to-server access. The flow:
- Register an application on the HMRC Developer Hub
- Receive a Client ID and Client Secret
- Request an access token from https://api.service.hmrc.gov.uk/oauth/token
- Include the token as a Bearer header on API requests
- Tokens expire (typically 4 hours), so you need to handle token refresh
The token request:
```
curl -X POST https://api.service.hmrc.gov.uk/oauth/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET"
```
The response:
```
{
"access_token": "HMRC_ACCESS_TOKEN",
"token_type": "bearer",
"expires_in": 14400
}
```
## Making a validation request
Once you have an access token, you can validate a VAT number. Note that HMRC strips the "GB" prefix, so you pass just the 9 digits:
```
curl https://api.service.hmrc.gov.uk/organisations/vat/check-vat-number/lookup/123456789 \
-H "Authorization: Bearer HMRC_ACCESS_TOKEN" \
-H "Accept: application/vnd.hmrc.2.0+json"
```
A successful response:
```
{
"target": {
"name": "ACME LTD",
"vatNumber": "123456789",
"address": {
"line1": "123 HIGH STREET",
"postcode": "SW1A 1AA",
"countryCode": "GB"
}
},
"processingDate": "2026-03-24T10:30:00Z"
}
```
Invalid numbers return a 404 (not a structured "valid: false" response). Your code needs to treat 404 as "not registered" rather than an error.
## Pain points of using HMRC directly
- OAuth 2.0 complexity: You need to manage the full token lifecycle (request, cache, refresh on expiry). For a simple validation check, this is a lot of overhead.
- Application approval: Getting production access requires submitting your application for review on the HMRC Developer Hub. The sandbox works immediately, but production approval can take days. During this time you cannot validate real VAT numbers.
- Sandbox vs production: HMRC has separate URLs for sandbox (test-api.service.hmrc.gov.uk) and production (api.service.hmrc.gov.uk), with separate credentials. You need to manage both environments.
- No batch endpoint: Like VIES, HMRC only validates one number per request. Bulk validation requires sequential requests with your own rate limit handling.
- Error responses: Invalid numbers return 404, not a structured validation result. Rate limits return 429. Server errors return 500.
- Accept header versioning: You must include the correct Accept header with the API version (e.g., application/vnd.hmrc.2.0+json). Getting this wrong results in cryptic 406 responses.
## How Avatcado simplifies UK VAT validation
Avatcado wraps HMRC (alongside VIES for EU, and national registries for CH, LI, NO, and AU) so you never deal with OAuth, token management, or environment-specific URLs. The same endpoint works for EU, UK, Swiss, Liechtenstein, Norwegian, and Australian numbers:
```
# EU number (routes to VIES)
curl https://api.avatcado.com/v1/validate?vat_number=DE123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
# UK number (routes to HMRC)
curl https://api.avatcado.com/v1/validate?vat_number=GB123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
Same response format, same error codes, same caching behavior. No OAuth dance, no environment switching, no application approval.
- Bearer token auth: Your Avatcado API key is all you need. No OAuth flow, no token refresh.
- Same response envelope for all countries: One consistent format regardless of whether the number routes to VIES, HMRC, or another national tax authority.
- 25-day caching with stale fallback: When HMRC is unavailable, cached results are returned with meta.cached: true.
- Test mode with magic numbers: GB111111111 always returns valid. No sandbox credentials, no approval process.
- Structured error responses: Machine-readable error codes instead of HTTP status guessing. Every error includes a code, message, and HTTP status.
For the EU equivalent, see the VIES API guide.
## Get started
Avatcado's free tier includes 500 validations per month, covering EU, UK, Swiss, Liechtenstein, Norwegian, and Australian numbers. No credit card required.
Start validating for free →
## FAQs
Q: Do I need OAuth to validate UK VAT numbers?
A: If you use the HMRC API directly, yes. HMRC requires OAuth 2.0 client credentials authentication with token management and refresh. Avatcado abstracts this entirely, so you use a simple Bearer token (your API key) for all validations regardless of country.
Q: How long does HMRC application approval take?
A: HMRC sandbox access is immediate, but production approval typically takes several business days. During this time, you cannot validate real UK VAT numbers through the HMRC API directly. Avatcado provides immediate access to UK validation with no approval process.
Q: Why does HMRC return 404 for invalid VAT numbers?
A: HMRC treats unregistered VAT numbers as 'not found' resources rather than returning a structured validation response. Your code needs to interpret a 404 as 'this number is not registered' rather than a server error. Avatcado normalizes this into a consistent response with valid: false.
Q: Can I validate Northern Ireland VAT numbers?
A: Northern Ireland uses the XI prefix and is validated through VIES (not HMRC), since Northern Ireland remains in the EU's single market for goods. Avatcado handles XI-prefixed numbers automatically, routing them to VIES.
## Sources
- Check a UK VAT Number API (HMRC Developer Hub): https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api
- Application-restricted endpoints (OAuth 2.0 client credentials) (HMRC Developer Hub): https://developer.service.hmrc.gov.uk/api-documentation/docs/authorisation/application-restricted-endpoints
- HMRC API reference guide (HMRC Developer Hub): https://developer.service.hmrc.gov.uk/api-documentation/docs/reference-guide
- Check a UK VAT number (online service) (GOV.UK): https://www.gov.uk/check-uk-vat-number
---
# How to Validate VAT Numbers in TypeScript
> Validate EU, UK, Swiss, Norwegian, and Australian VAT/GST numbers in TypeScript with the @avatcado/node SDK. Covers error handling, batch validation, test mode, and framework integration.
Published: 2026-03-24
Updated: 2026-03-24
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/validate-vat-number-typescript
This guide covers how to validate EU, UK, Swiss, Liechtenstein, Norwegian, and Australian VAT/GST numbers in TypeScript using the @avatcado/node SDK. The SDK provides typed responses, a clean error handling pattern, batch validation, and test mode support. We will also show raw fetch() equivalents so you can see what the SDK abstracts away.
## Installation
```
npm install @avatcado/node
```
## Basic validation
The simplest possible example:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
const { data, error } = await avatcado.vat.validate({
vatNumber: "DE123456789",
});
if (data?.data.valid) {
console.log(`Company: ${data.data.company?.name}`);
console.log(`Country: ${data.data.countryCode}`);
}
```
The SDK returns { data, error }. Exactly one of these is set, never both. data contains the full validation result. error contains a structured error with code and message. The SDK never throws exceptions for API errors.
## Error handling
```
const { data, error } = await avatcado.vat.validate({
vatNumber: "INVALID",
});
if (error) {
switch (error.code) {
case "invalid_vat_format":
// The input is not a valid VAT number format
console.error("Bad format:", error.message);
break;
case "upstream_unavailable":
// VIES or HMRC is down
console.error("Upstream down, try again later");
break;
case "rate_limit_exceeded":
// Monthly quota exceeded
console.error("Quota exceeded");
break;
default:
console.error(`Error: ${error.code} - ${error.message}`);
}
return;
}
// data is guaranteed to be set here
console.log(data.data.valid, data.data.vatNumber);
```
All error codes are documented at docs.avatcado.com with explanations and suggested handling. Every error includes a docs_url field pointing to the specific error page.
## Batch validation
```
import Avatcado, { isBatchSuccess } from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
const { data, error } = await avatcado.vat.validateBatch({
vatNumbers: [
"DE123456789",
"FR82542065479",
"GB987654321",
"INVALID123",
],
});
if (error) {
console.error("Batch request failed:", error.message);
return;
}
for (const result of data.data.results) {
if (isBatchSuccess(result)) {
console.log(`${result.data.vatNumber}: ${result.data.valid ? "valid" : "invalid"}`);
} else {
console.log(`${result.meta.vatNumber}: error - ${result.error.code}`);
}
}
console.log(`${data.data.summary.succeeded}/${data.data.summary.total} succeeded`);
```
Batch validation accepts up to 50 VAT numbers in a single request. Available on Pro and Business tiers. The isBatchSuccess() type guard narrows the result type so TypeScript knows whether you have data or error on each item. Results are returned in the same order as the input.
## Test mode
```
// Use a test key - no upstream calls, no quota usage
const avatcado = new Avatcado("avat_test_your_test_key");
// Magic numbers for deterministic testing
const valid = await avatcado.vat.validate({ vatNumber: "DE111111111" }); // Always valid
const invalid = await avatcado.vat.validate({ vatNumber: "DE000000000" }); // Always invalid
const down = await avatcado.vat.validate({ vatNumber: "DE999999999" }); // Simulates upstream outage
const stale = await avatcado.vat.validate({ vatNumber: "DE555555555" }); // Stale cache response
```
DE111111111 always returns valid with a test company. DE000000000 always returns invalid. DE999999999 simulates an upstream service outage. DE555555555 returns a stale cached response. Test mode is ideal for CI/CD pipelines. You get fast, deterministic responses without hitting VIES or HMRC.
## Comparison: SDK vs raw fetch
Here's the same basic validation using raw fetch():
```
const response = await fetch(
"https://api.avatcado.com/v1/validate?vat_number=DE123456789",
{
headers: {
Authorization: "Bearer avat_live_your_api_key",
},
}
);
const result = await response.json();
if (response.ok) {
const { data, meta } = result;
console.log(data.valid, data.company?.name);
} else {
const { error, meta } = result;
console.error(error.code, error.message);
}
```
The raw fetch approach works fine, but the SDK adds typed responses, automatic error parsing, batch support with type guards, and a cleaner API. If you prefer minimal dependencies, the REST API is straightforward to call directly.
Or with curl:
```
curl https://api.avatcado.com/v1/validate?vat_number=DE123456789 \
-H "Authorization: Bearer avat_live_your_api_key"
```
## Framework integration examples
### Next.js API route
```
import Avatcado from "@avatcado/node";
import { NextRequest, NextResponse } from "next/server";
const avatcado = new Avatcado(process.env.AVATCADO_API_KEY!);
export async function GET(request: NextRequest) {
const vatNumber = request.nextUrl.searchParams.get("vat_number");
if (!vatNumber) {
return NextResponse.json(
{ error: "vat_number is required" },
{ status: 400 }
);
}
const { data, error } = await avatcado.vat.validate({ vatNumber });
if (error) {
return NextResponse.json({ error }, { status: 422 });
}
return NextResponse.json({ data });
}
```
### Express middleware
```
import Avatcado from "@avatcado/node";
import type { Request, Response, NextFunction } from "express";
const avatcado = new Avatcado(process.env.AVATCADO_API_KEY!);
export async function validateVat(req: Request, res: Response, next: NextFunction) {
const vatNumber = req.body.vat_number;
if (!vatNumber) return next();
const { data, error } = await avatcado.vat.validate({ vatNumber });
if (error) {
res.status(422).json({ error: error.message });
return;
}
req.vatValidation = data;
next();
}
```
### Hono handler
```
import { Hono } from "hono";
import Avatcado from "@avatcado/node";
const app = new Hono();
const avatcado = new Avatcado(process.env.AVATCADO_API_KEY!);
app.get("/validate", async (c) => {
const vatNumber = c.req.query("vat_number");
if (!vatNumber) {
return c.json({ error: "vat_number is required" }, 400);
}
const { data, error } = await avatcado.vat.validate({ vatNumber });
if (error) {
return c.json({ error }, 422);
}
return c.json({ data });
});
```
## Python SDK
Looking for Python? The avatcado package on PyPI offers the same features with typed exceptions and async support. Install with pip install avatcado.
## Get started
The free tier includes 500 validations per month. Install the SDK, grab a test key, and start validating. See the full API reference at docs.avatcado.com.
Get your API key →
## FAQs
Q: Does the @avatcado/node SDK work with Bun and Deno?
A: The SDK uses standard fetch under the hood and requires Node.js 18 or later (it has zero dependencies and uses the native fetch API). Other runtimes with a compatible fetch implementation generally work, but only Node.js is officially supported.
Q: Can I use the Avatcado API without the SDK?
A: Yes. The REST API is straightforward to call with fetch, axios, or any HTTP client. The SDK adds typed responses, automatic error parsing, and batch type guards, but the API works the same way with raw HTTP requests.
Q: How does test mode work?
A: Use an API key prefixed with avat_test_ instead of avat_live_. Test mode uses magic VAT numbers (like DE111111111 for valid, DE000000000 for invalid) to return deterministic responses without hitting VIES or HMRC. No quota is consumed in test mode.
Q: What is the isBatchSuccess type guard?
A: isBatchSuccess() is a TypeScript type guard exported by @avatcado/node. It narrows a batch result item to the success type (with data) or error type (with error). This lets TypeScript statically verify you are accessing the correct fields on each batch item.
## Sources
- @avatcado/node on npm (npm): https://www.npmjs.com/package/@avatcado/node
- avatcado on PyPI (PyPI): https://pypi.org/project/avatcado/
- Node.js 18 release notes (global fetch) (OpenJS Foundation): https://nodejs.org/en/blog/announcements/v18-release-announce
- Avatcado API documentation (Avatcado): https://docs.avatcado.com
---
# VIES Downtime: Why It Happens and How to Handle It
> Why VIES goes down, which EU countries are least reliable, and how to keep your VAT validation working during outages with caching and stale fallback.
Published: 2026-03-24
Updated: 2026-03-24
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vies-downtime-and-how-to-handle-it
If you validate EU VAT numbers, you depend on VIES. And VIES goes down. Not occasionally, but regularly, and often for specific countries rather than the entire system. This guide explains why VIES downtime happens, how it affects your application, and what you can do about it.
## Why VIES goes down
VIES is not a single database. It is a gateway operated by the European Commission that routes validation requests to 27 independent national tax authority databases. When you validate a French VAT number, VIES forwards your request to the French tax authority (DGFiP). When you validate an Italian number, it goes to Italy's Agenzia delle Entrate.
This means VIES availability is only as good as the weakest link in any given request. Each member state maintains its own infrastructure with its own uptime characteristics, maintenance windows, and capacity limits.
## Common offenders
In our own VIES uptime monitoring, some member states have been less reliable than others:
- Italy: Frequent maintenance windows, sometimes during business hours. Agenzia delle Entrate (the Italian tax authority that powers VIES for IT VAT numbers) has regular planned outages on its VAT registry service.
- Spain: The Agencia Tributaria service experiences intermittent availability, particularly during tax filing periods.
- Greece: AADE (Independent Authority for Public Revenue) has had extended outages lasting hours.
- Belgium: Occasional rate limiting issues that cause cascading failures.
In the same monitoring data, Germany, the Netherlands, and the Nordic countries tend to have the most reliable national services.
You can monitor current and historical availability on our VIES uptime monitor.
## What happens to your application
When a member state is down and you call VIES directly, you get a SOAP fault with code MS_UNAVAILABLE or TIMEOUT. If your code is not built to handle this, your checkout flow, invoice generation, or customer onboarding breaks. The user sees an error and either retries (adding load) or abandons the process.
This is particularly painful for SaaS applications where VAT validation happens at signup or checkout. A 30-minute outage in one country means you cannot process any customers from that country for 30 minutes.
## Strategies for handling downtime
### Build your own cache
Cache validation results on your side with a TTL (e.g., 24 hours). When VIES is down, serve the cached result. Trade-offs: you need to manage cache storage, decide on an acceptable staleness window, and handle cache misses for numbers you have never seen before. A 24-hour cache is generally reasonable because VAT registrations do not change frequently.
### Retry with backoff
Implement exponential backoff on VIES failures. This helps with transient issues but does not solve sustained outages. If a member state is down for an hour, your users are still waiting. Retries are a good complement to caching, not a replacement.
### Use a wrapper API
Instead of calling VIES directly, use an API that handles caching, retries, and fallback logic for you. This is what Avatcado does.
## How Avatcado handles VIES downtime
Avatcado caches every validation result for 25 days. When a member state goes down, Avatcado serves the cached result transparently. The response tells you exactly what happened through the meta fields.
A normal (live) response:
```
curl https://api.avatcado.com/v1/validate?vat_number=IT12345678901 \
-H "Authorization: Bearer avat_live_your_api_key"
```
```
{
"data": {
"valid": true,
"vat_number": "IT12345678901",
"country_code": "IT",
"company": {
"name": "ESEMPIO S.R.L.",
"address": "VIA ROMA 1, 00100 ROMA"
},
"consultation_number": null,
"requested_at": "2026-03-24T10:30:00Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"request_duration_ms": 1420,
"source_status": "live"
}
}
```
A stale cache response (when Italy is down):
```
{
"data": {
"valid": true,
"vat_number": "IT12345678901",
"country_code": "IT",
"company": {
"name": "ESEMPIO S.R.L.",
"address": "VIA ROMA 1, 00100 ROMA"
},
"consultation_number": null,
"requested_at": "2026-02-25T14:00:00Z"
},
"meta": {
"request_id": "661f9500-f30c-52e5-b827-557766551111",
"request_duration_ms": 45,
"cached": true,
"cached_at": "2026-02-25T14:00:00Z",
"stale": true,
"source_status": "unavailable"
}
}
```
Here's what each meta field tells you:
- cached: true means this result came from cache, not a live lookup.
- cached_at tells you when the original lookup happened.
- stale: true means the cache entry has expired (older than 25 days) but is being served because the upstream is unavailable.
- source_status: "unavailable" means the member state service was down when Avatcado tried to reach it.
- source_status: "degraded" means VIES returned a result but Avatcado detected a possible silent false negative (some member states return "invalid" for valid numbers during partial outages).
## Handling meta fields in your code
Here's how you might use these fields in a TypeScript application:
```
const response = await fetch(
"https://api.avatcado.com/v1/validate?vat_number=IT12345678901",
{ headers: { Authorization: "Bearer avat_live_your_api_key" } }
);
const { data, meta } = await response.json();
if (data.valid) {
if (meta.stale) {
// Result is valid but based on stale cache
// Consider flagging for re-validation later
console.log("Valid (stale cache, upstream was down)");
} else {
console.log("Valid (fresh result)");
}
}
// Log source status for monitoring
if (meta.source_status === "unavailable") {
console.warn(`VIES unavailable for ${data.country_code}`);
} else if (meta.source_status === "degraded") {
console.warn(`VIES degraded for ${data.country_code}, result may be unreliable`);
}
```
## Get started
Stop building VIES reliability infrastructure yourself. Avatcado handles caching, retries, and stale fallback so your application stays up even when VIES does not.
Monitor VIES availability in real-time on our uptime monitor. Read the VIES API guide for more on how VIES works under the hood.
Avatcado's free tier includes 500 validations per month with the same caching and reliability features as paid plans.
Start validating for free →
## FAQs
Q: How often does VIES go down?
A: VIES experiences partial outages (individual member states) almost daily. Full system outages are rare. The frequency varies by country: Italy and Spain have regular maintenance windows, while Germany and the Netherlands are typically stable. Check the Avatcado VIES uptime monitor for current data.
Q: What does source_status: 'degraded' mean?
A: Degraded status means VIES returned a response, but Avatcado detected a likely silent false negative. Some member states return 'invalid' for valid VAT numbers during partial outages instead of returning an error. Avatcado compares against cached results to detect this and serves the cached (correct) result instead.
Q: Is it safe to accept a stale cached VAT validation?
A: Generally yes. VAT registrations rarely change without notice, and a 25-day-old result is almost always still accurate. For high-value transactions, you may want to flag stale results for re-validation once the upstream service recovers. The meta.stale field lets you implement this logic.
Q: What happens when VIES is down for a number I have never validated?
A: If there is no cached result and the upstream is unavailable, Avatcado returns a 503 error with the code upstream_unavailable or upstream_member_state_unavailable. Your application should handle this gracefully, for example by letting the customer proceed and re-validating later.
## Sources
- VIES on the Web: help and availability (European Commission): https://ec.europa.eu/taxation_customs/vies/#/help
- VIES checkVatService WSDL (European Commission): https://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl
- EU countries (European Union): https://european-union.europa.eu/principles-countries-history/eu-countries_en
---
# Swiss VAT Number Validation API: CHE Format
> How to validate Swiss and Liechtenstein VAT numbers using the BFS UID Register. Covers the CHE format, MWST/TVA/IVA suffixes, MOD11 checksum, and the difference between a valid UID and active VAT registration.
Published: 2026-03-26
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/swiss-vat-number-validation
Switzerland and Liechtenstein use their own VAT system, separate from the EU's VIES. Swiss VAT numbers are managed by the Federal Statistical Office (BFS) through the UID Register. This guide covers how Swiss VAT numbers work, how to validate them, and how Avatcado handles it for you.
## What is a Swiss VAT number?
Swiss VAT numbers follow the UID (Unternehmens-Identifikationsnummer) format. The full format is CHE-NNN.NNN.NNN MWST, where:
- CHE is the ISO 3166-1 alpha-3 country code for Switzerland
- The 9 digits form the unique identifier, where the 9th digit is a MOD11 checksum
- The suffix indicates the language region: MWST (German), TVA (French), or IVA (Italian)
All three suffixes are equivalent. A business with CHE-123.456.788 MWST is the same entity as CHE-123.456.788 TVA. For the full format reference with a free checker, see the Swiss VAT number format page.
## UID vs VAT registration
An important distinction: having a UID does not mean a business is VAT-registered. The UID Register tracks all Swiss enterprises, but only some are registered for VAT (MWST/TVA/IVA). A business can have a valid UID but not be VAT-registered if their revenue is below the CHF 100,000 threshold.
When you validate a Swiss VAT number, you need to check both: (1) does the UID exist, and (2) is the entity actively registered for VAT? Avatcado checks both and only returns valid: true when the entity has active VAT registration.
## The BFS UID Register API
The Swiss Federal Statistical Office (BFS) provides a public SOAP API for UID lookups. It requires no authentication, but has a strict rate limit of 20 requests per minute. The API uses eCH Swiss e-government standards for its XML schema. You can also look up individual entries manually through the public UID register.
Key details:
- Endpoint: https://www.uid-wse.admin.ch/V3.0/PublicServices.svc
- Protocol: SOAP 1.1
- Authentication: none (public)
- Rate limit: 20 requests per minute per caller
- Returns: company name, address, VAT registration status, legal form
## MOD11 checksum validation
The 9th digit of a Swiss UID is a MOD11 checksum. Before calling the API, you can validate the format locally. The algorithm uses weights [5, 4, 3, 2, 7, 6, 5, 4] on digits 1-8, takes the sum modulo 11, and the check digit is 11 - remainder. If the remainder is 1, the number is invalid (no valid check digit exists).
## Liechtenstein: same system
Liechtenstein and Switzerland form a common VAT territory. Liechtenstein businesses are registered in the same Swiss UID Register with CHE-format numbers. When you validate a Liechtenstein VAT number through Avatcado, it routes to the same BFS API but returns country_code: "LI" in the response.
## Validating with Avatcado
Avatcado handles all of this automatically. Pass any Swiss or Liechtenstein VAT number to the same endpoint you use for EU, UK, Norwegian, and Australian numbers:
```
curl "https://api.avatcado.com/v1/validate?vat_number=CHE-123.456.788%20MWST" \
-H "Authorization: Bearer avat_live_YOUR_KEY"
```
Response:
```
{
"data": {
"valid": true,
"vat_number": "CH123456788",
"country_code": "CH",
"company": {
"name": "Example AG",
"address": "Bahnhofstrasse 1, 8001 Zurich, CH"
},
"requested_at": "2026-03-26T12:00:00.000Z"
},
"meta": {
"request_id": "req_abc123"
}
}
```
Avatcado normalizes all input formats automatically:
- CHE-123.456.788 MWST, CHE123456788TVA, CHE123456788, CH123456788 all work
- Dashes, dots, spaces, and suffixes are stripped
- MOD11 checksum is validated before making the upstream call
- Results are cached for 25 days to stay within the BFS rate limit
## Swiss VAT rates
Switzerland has three VAT rates: 8.1% (standard), 3.8% (accommodation), and 2.6% (food, books, medicines). Liechtenstein uses the same rates. Both are available through the /v1/rates endpoint; see current Swiss VAT rates for the full breakdown.
## Test mode
Use a avat_test_ API key with these magic numbers:
- CH111111118 - valid, VAT-registered
- CH222222225 - valid UID, not VAT-registered
- LI111111118 - valid (LI numbers are normalized to CH before lookup, so test mode returns country_code: "CH" for this number)
- CH999999996 - simulates upstream error (503)
Get your API key and start validating Swiss VAT numbers in under 2 minutes.
## FAQs
Q: What is the difference between a Swiss UID and a VAT number?
A: A UID (Unternehmens-Identifikationsnummer) is assigned to all Swiss enterprises. A VAT number is a UID with an active MWST/TVA/IVA registration. A business can have a valid UID without being VAT-registered if their revenue is below CHF 100,000.
Q: Can I validate Liechtenstein VAT numbers with the same API?
A: Yes. Liechtenstein and Switzerland share a common VAT territory. Liechtenstein businesses are registered in the Swiss UID Register with CHE-format numbers. Avatcado validates both through the same endpoint.
Q: What are the Swiss VAT rate suffixes MWST, TVA, and IVA?
A: They are the same thing in different languages. MWST is German (Mehrwertsteuer), TVA is French (Taxe sur la valeur ajoutee), and IVA is Italian (Imposta sul valore aggiunto). All three are valid suffixes for the same VAT number.
Q: Why does the Swiss UID Register have a rate limit?
A: The BFS UID Register allows 20 requests per minute per caller. This is a hard limit. Avatcado caches results for 25 days, so most lookups never hit the upstream API. For high-volume Swiss validation, caching is critical.
## Sources
- UID web services (Swiss Federal Chancellery): https://www.bk.admin.ch/bk/de/home/digitale-transformation-ikt-lenkung/e-services-bund/services/uid-webservice.html
- eCH-0097 data standard (UID check digit) (Verein eCH): https://www.ech.ch/sites/default/files/dosvers/hauptdokument/STAN_d_REP_2015-11-26_eCH-0097_V2.0_Datenstandard%20Unternehmensidentifikation.pdf
- VAT rates in Switzerland (Swiss Federal Tax Administration): https://www.estv.admin.ch/estv/en/home/value-added-tax/vat-rates-switzerland.html
- VAT tax liability (CHF 100,000 threshold) (Swiss Federal Tax Administration): https://www.estv.admin.ch/estv/en/home/value-added-tax/vat-tax-liability.html
---
# Norwegian VAT Number Validation API: MVA Format
> How to validate Norwegian VAT numbers (MVA) using the Bronnoysund Register Centre API. Covers the organization number format, MOD11 checksum, SDK integration, batch validation, and limitations.
Published: 2026-03-26
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/norwegian-vat-number-validation
Norwegian VAT numbers (MVA numbers) are based on the 9-digit organization number issued by the Bronnoysund Register Centre (Enhetsregisteret). This guide covers how Norwegian VAT numbers work, how validation works, and how Avatcado handles it for you.
## What is a Norwegian VAT number?
A Norwegian VAT number consists of the country prefix NO, a 9-digit organization number, and the suffix MVA (Merverdiavgift, the Norwegian word for VAT). The full format is NO123456785MVA.
- NO - country prefix
- 9 digits - organization number (last digit is a MOD11 checksum)
- MVA - indicates VAT registration
The organization number itself is assigned by the Bronnoysund Register Centre and is used for all official purposes in Norway, not just VAT. For the full format reference with a free checker, see the Norwegian VAT number format page.
## Organization number vs MVA registration
Similar to the Swiss system, having a valid organization number does not mean the entity is VAT-registered. A company must register for MVA separately once their taxable turnover exceeds NOK 50,000. The Enhetsregisteret tracks both: the entity exists in the register, and the registrertIMvaregisteret field indicates whether they are MVA-registered.
Avatcado checks both. valid: true means the entity exists and is actively registered for MVA.
## The Bronnoysund Register API
The Bronnoysund Register Centre (Bronnoysundregistrene) provides a free, open REST API for looking up Norwegian organizations at data.brreg.no. No authentication is required.
- Endpoint: GET https://data.brreg.no/enhetsregisteret/api/enheter/{orgNumber}
- Protocol: REST (JSON)
- Authentication: none
- No documented rate limit
- Returns: company name, address, MVA registration status, business activity codes
HTTP status codes, per the Enhetsregisteret API documentation:
- 200 - entity found (check registrertIMvaregisteret for VAT status)
- 404 - organization number does not exist
- 410 - entity was removed for legal reasons (Fjernet av juridiske arsaker)
## MOD11 checksum validation
The 9th digit of a Norwegian organization number is a MOD11 checksum. The algorithm uses weights [3, 2, 7, 6, 5, 4, 3, 2] on digits 1-8. If the remainder is 1, the number is invalid. If the remainder is 0, the check digit is 0.
## Validating with Avatcado
Pass any Norwegian VAT number to the same endpoint you use for EU, UK, and Swiss numbers:
```
curl "https://api.avatcado.com/v1/validate?vat_number=NO923609016MVA" \
-H "Authorization: Bearer avat_live_YOUR_KEY"
```
Response:
```
{
"data": {
"valid": true,
"vat_number": "NO923609016",
"country_code": "NO",
"company": {
"name": "EQUINOR ASA",
"address": "Forusbeen 50, 4035 STAVANGER"
},
"requested_at": "2026-03-26T12:00:00.000Z"
},
"meta": {
"request_id": "req_abc123"
}
}
```
Avatcado normalizes all input formats:
- NO923609016MVA, NO923609016, and 923609016 with an NO prefix all work
- The MVA suffix is stripped automatically
- MOD11 checksum is validated before making the upstream call
- Results are cached for 25 days
## SDK integration
The @avatcado/node SDK works the same way for Norwegian numbers as it does for every other country:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_YOUR_KEY");
const { data, error } = await avatcado.vat.validate({
vatNumber: "NO923609016MVA",
});
if (error) {
console.error(error.code, error.message);
} else if (data.data.valid) {
console.log(data.data.company?.name, data.data.company?.address);
} else {
// Org number exists but is not registered for MVA, or does not exist at all
console.log("Not a valid MVA registration");
}
```
The avatcado Python package mirrors the same shape:
```
from avatcado import Avatcado
avatcado = Avatcado("avat_live_YOUR_KEY")
result = avatcado.vat.validate("NO923609016MVA")
if result.data.valid:
print(result.data.company.name)
```
Onboarding several Norwegian suppliers at once? Use the batch endpoint (Pro and Business plans) instead of looping single requests:
```
curl -X POST "https://api.avatcado.com/v1/validate/batch" \
-H "Authorization: Bearer avat_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"vat_numbers": [
"NO923609016MVA",
"NO982463718MVA"
]
}'
```
## Limitations
- No consultation numbers. Consultation numbers are a VIES-specific concept for EU cross-border audit proof. Norway is not in VIES, so there is no Norwegian equivalent. Store the raw API response and timestamp as your audit trail instead.
- Undocumented upstream rate limits. The Bronnoysund Register does not publish a rate limit, which means it could change without notice. Avatcado's 25-day cache keeps most repeat lookups from ever reaching the upstream API, insulating you from this uncertainty.
- 410 vs 404 both mean invalid. Per the Enhetsregisteret API documentation, the register distinguishes between an organization number that never existed (404) and an entity that was removed from the register for legal reasons (410). Avatcado normalizes both to valid: false, so if you need to distinguish "never existed" from "removed" for compliance reasons, you will need to call the Bronnoysund API directly for that specific case.
- MVA threshold differs from the EU. The NOK 50,000 registration threshold does not map directly to any EU country's VAT registration threshold. Do not assume a Norwegian counterpart to an EU reverse-charge rule without checking Norwegian tax guidance directly.
## Norwegian VAT rates
Norway has three main VAT rates: 25% (standard), 15% (food), and 12% (transport, accommodation, cinema). These are available through the /v1/rates endpoint; see current Norwegian VAT rates for the full breakdown.
## Test mode
Use a avat_test_ API key with these magic numbers:
- NO123456785 - valid, MVA-registered
- NO987654325 - valid org, not MVA-registered
- NO999999999 - simulates upstream error (503)
Get your API key and start validating Norwegian VAT numbers in under 2 minutes.
## FAQs
Q: What is a Norwegian MVA number?
A: An MVA (Merverdiavgift) number is a Norwegian organization number with the prefix NO and suffix MVA. The format is NO + 9 digits + MVA, for example NO923609016MVA. The 9-digit part is the organization number assigned by the Bronnoysund Register Centre.
Q: Is the Bronnoysund Register API free to use?
A: Yes. The Enhetsregisteret API is completely free, requires no authentication, and has no documented rate limit. It returns JSON and is straightforward to integrate with.
Q: What does a 410 response from the Bronnoysund API mean?
A: HTTP 410 (Gone) means the organization existed in the register but has been deleted. This is different from 404 (never existed). Avatcado treats both as invalid and returns valid: false.
Q: Can a Norwegian company have an organization number but not be MVA-registered?
A: Yes. All Norwegian companies get an organization number, but MVA registration is separate and only required when taxable turnover exceeds NOK 50,000. Avatcado checks the registrertIMvaregisteret field and only returns valid: true for actively registered businesses.
## Sources
- Enhetsregisteret API documentation (Bronnoysund Register Centre): https://data.brreg.no/enhetsregisteret/api/docs/index.html
- About the organisation number (modulus 11 check digit) (Bronnoysund Register Centre): https://www.brreg.no/en/about-us-2/our-registers/about-the-central-coordinating-register-for-legal-entities-ccr/about-the-organisation-number/
- Register in the VAT Register (NOK 50,000 threshold) (Norwegian Tax Administration): https://www.skatteetaten.no/en/business-and-organisation/vat-and-duties/vat/register-change-delete/
- VAT rates (Norwegian Tax Administration): https://www.skatteetaten.no/en/rates/value-added-tax/
---
# Australian GST Validation API: ABN and ABR Lookup
> How to validate Australian ABNs and GST registration using the ABR Lookup service. Covers the ABN format, weighted checksum algorithm, checkout integration, batch validation, and edge case handling.
Published: 2026-03-26
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/australian-gst-number-validation
Australian GST validation is based on the Australian Business Number (ABN), an 11-digit identifier issued by the Australian Business Register (ABR). This guide covers how ABNs and GST registration work, and how Avatcado validates them for you.
## What is an ABN?
An ABN (Australian Business Number) is an 11-digit unique identifier for businesses operating in Australia. It is issued by the Australian Business Register and is required for tax and business transactions. In Avatcado, Australian ABNs use the AU country prefix. Example: AU51824753556.
- AU - country prefix (Avatcado convention)
- 11 digits - the ABN itself (first two are check digits)
You can also pass the ABN prefix (e.g., ABN51824753556) and Avatcado normalizes it to AU51824753556 automatically. For the full format reference with a free checker, see the Australian ABN format page.
## ABN vs GST registration
Having an ABN does not automatically mean the business is registered for GST (Goods and Services Tax). Businesses must register for GST separately once their annual turnover reaches AUD 75,000 (AUD 150,000 for non-profit organizations).
Avatcado checks both: valid: true means the ABN exists and the business has an active GST registration. If the ABN exists but has no GST registration, you get valid: false with the company name and address still included.
## The ABR Lookup service
The Australian Business Register provides a public API for looking up ABN details. Key characteristics:
- Protocol: JSONP (callback wrapper that needs to be stripped)
- Authentication: requires a GUID (API key) obtained by registration
- Returns: business name, ABN status, GST registration date, address (state + postcode)
- Free to use
The ABR API returns GST registration as a date field. If the field contains a date, the business is currently GST-registered. If it is empty, the business is not GST-registered.
## Why wrapping the ABR matters
The ABR API has several characteristics that make direct integration painful:
- JSONP response format requires stripping the callback wrapper before parsing
- Errors come back as a plain Message string
- No documented rate limits or caching guidance
- Authentication requires registering for a GUID
Avatcado wraps the ABR in the same REST API, JSON format, caching layer, and error handling you already use for EU, UK, and other validations. No additional integration work required.
## ABN checksum validation
ABNs include a checksum for detecting data entry errors. The algorithm works differently from MOD11 used by Swiss and Norwegian numbers:
- Subtract 1 from the first digit
- Multiply each of the 11 digits by the weights [10, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
- Sum all products
- The ABN is valid if the sum is divisible by 89
Avatcado validates this checksum before calling the ABR upstream. Invalid checksums return a 422 invalid_vat_format error immediately, saving you an unnecessary API call.
## Validating with Avatcado
Pass any Australian ABN to the same endpoint you use for EU, UK, Swiss, and Norwegian numbers:
```
curl "https://api.avatcado.com/v1/validate?vat_number=AU51824753556" \
-H "Authorization: Bearer avat_live_YOUR_KEY"
```
Response:
```
{
"data": {
"valid": true,
"vat_number": "AU51824753556",
"country_code": "AU",
"company": {
"name": "AUSTRALIAN TAXATION OFFICE",
"address": "NSW 2640"
},
"requested_at": "2026-03-26T12:00:00.000Z"
},
"meta": {
"request_id": "req_abc123"
}
}
```
Avatcado normalizes all input formats:
- AU51824753556, ABN51824753556, and abn51824753556 all work
- ABN 51 824 753 556 with spaces is also accepted; spaces and dots are stripped automatically
- The ABN prefix is converted to AU automatically, and lowercase input is uppercased
- Checksum is validated before making the upstream call
- Results are cached for 25 days
## Using ABN validation in a checkout flow
For B2B transactions in Australia, collecting and verifying a customer's ABN at checkout is standard practice. Check data.valid in the response. If true, the business has an active GST registration and you can apply the appropriate tax treatment. If false, the ABN exists but the business is not GST-registered, and you should not zero-rate the transaction.
## Handling edge cases
- Invalid ABN checksum: Avatcado validates the checksum before hitting the ABR. If it fails, you get a 422 invalid_vat_format error immediately, catching typos without a network round-trip.
- ABN exists but no GST: The response includes valid: false with company.name and company.address populated. The business exists but is not GST-registered.
- ABN not found: The response includes valid: false with company: null. The ABN does not exist in the ABR.
- ABR is down: Avatcado serves a cached result with meta.stale: true if one exists. Otherwise, a 503 upstream_unavailable error is returned. Implement retry logic with exponential backoff for transient failures.
## Batch validation for vendor onboarding
When onboarding multiple Australian vendors, use the batch endpoint to validate up to 50 ABNs in a single request (Pro and Business plans):
```
curl -X POST "https://api.avatcado.com/v1/validate/batch" \
-H "Authorization: Bearer avat_live_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"vat_numbers": [
"AU51824753556",
"AU53004085616",
"AU33102417032"
]
}'
```
Each item in the response has its own data and meta block, so you can see which ABNs are valid and which failed independently of the others.
## SDK integration
The @avatcado/node SDK covers single and batch validation:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_YOUR_KEY");
const { data, error } = await avatcado.vat.validate({
vatNumber: "AU51824753556",
});
if (error) {
console.error(error.code, error.message);
} else {
console.log(data.data.valid, data.data.company?.name);
}
const batch = await avatcado.vat.validateBatch({
vatNumbers: ["AU51824753556", "AU53004085616"],
});
```
The avatcado Python package provides the same functionality with typed exceptions:
```
from avatcado import Avatcado
avatcado = Avatcado("avat_live_YOUR_KEY")
result = avatcado.vat.validate("AU51824753556")
if result.data.valid:
print(result.data.company.name)
batch = avatcado.vat.validate_batch(["AU51824753556", "AU53004085616"])
```
## Australian GST rates
Australia has a flat 10% GST rate with no reduced rates. GST-free items (fresh food, health, education) are zero-rated. Note that the /v1/rates endpoint also returns the current Australian GST rate alongside the other supported countries.
## Test mode
Use a avat_test_ API key with any valid ABN checksum. For example:
- AU51824753556 - returns valid with test company data
Any ABN that passes the checksum validation will return a test response in test mode. Test mode never hits the ABR, so your integration tests are fast and deterministic.
Get your API key and start validating Australian ABNs in under 2 minutes.
## FAQs
Q: What is the difference between an ABN and a GST number?
A: An ABN (Australian Business Number) is an 11-digit identifier for all Australian businesses. GST registration is separate and only required for businesses with annual turnover above AUD 75,000. A business can have a valid ABN without being registered for GST.
Q: Is the ABR Lookup API free?
A: Yes. The ABR provides a free lookup service. You need to register for a GUID (API key) to access it, but there are no per-request charges.
Q: Can a business have an ABN but not be registered for GST?
A: Yes. All Australian businesses need an ABN for tax and government purposes, but GST registration is only mandatory when taxable turnover exceeds AUD 75,000 (AUD 150,000 for non-profits). Avatcado returns valid: false with the company name included when a business has an ABN but no GST registration.
Q: What format should I use when validating an Australian business number?
A: Use the AU prefix followed by 11 digits (e.g., AU51824753556). You can also pass the ABN prefix (e.g., ABN51824753556) and Avatcado normalizes it automatically. Spaces and dots are stripped.
Q: How do I validate an ABN in my checkout flow?
A: Pass the ABN with an AU prefix to the Avatcado validate endpoint. If data.valid is true, the business has an active GST registration. If false, the ABN exists but has no GST registration. Handle the 422 error for invalid checksum to catch typos before the API call.
Q: Can I validate multiple ABNs at once?
A: Yes. The batch endpoint accepts up to 50 VAT/GST numbers per request, including Australian ABNs. Each item returns independently, so one failed ABN does not block the others. Batch validation is available on Pro and Business plans.
Q: What happens if the ABR is down?
A: Avatcado serves a cached result with meta.stale: true if one exists. If no cached result is available, a 503 upstream_unavailable error is returned. Implement retry logic with exponential backoff for transient failures.
## Sources
- ABN format and checksum (Australian Business Register): https://abr.business.gov.au/Help/AbnFormat
- ABN Lookup web services (Australian Business Register): https://abr.business.gov.au/Tools/WebServices
- Registering for GST (AUD 75,000 / 150,000 thresholds) (Australian Taxation Office): https://www.ato.gov.au/businesses-and-organisations/gst-excise-and-indirect-taxes/gst/registering-for-gst
- GST-free sales (Australian Taxation Office): https://www.ato.gov.au/businesses-and-organisations/gst-excise-and-indirect-taxes/gst/when-to-charge-gst-and-when-not-to/gst-free-sales
---
# VAT Validation for Mollie Merchants
> Mollie does not validate customer VAT numbers. Learn how to add VAT validation to your Mollie checkout flow for B2B reverse charge compliance using Avatcado.
Published: 2026-03-30
Updated: 2026-03-30
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-mollie
If you use Mollie as your payment provider and sell B2B in the EU, you need to validate your customers' VAT numbers to apply the reverse charge mechanism correctly. Mollie handles payments but does not validate customer VAT numbers. You need a separate solution.
This guide covers why VAT validation matters for Mollie merchants and how to add it to your checkout flow.
## Why Mollie doesn't handle VAT validation
Mollie is a payment processor, not a tax platform. It processes iDEAL, Bancontact, SEPA, credit cards, Klarna, and other payment methods. It does not validate customer tax IDs, calculate VAT, or automate the reverse charge mechanism.
Unlike Stripe (which offers limited built-in validation for EU, UK, and AU), Mollie has no equivalent feature. Mollie checks your own VAT number during merchant onboarding via VIES, but that's it. For customer VAT validation, you're on your own.
## When do Mollie merchants need VAT validation?
You need VAT validation whenever you sell B2B across EU borders and want to apply the reverse charge mechanism. Since January 2020, a valid customer VAT number is a legal requirement for zero-rating intra-Community supplies.
- B2B cross-border sales within the EU (reverse charge requires a valid VAT number)
- SaaS, digital services, or physical goods sold B2B to customers in other EU countries
- Without validation, you either charge VAT when you shouldn't (losing the sale or annoying the customer) or skip VAT when you should (creating a tax liability)
## How to add VAT validation to your Mollie checkout
The flow is simple: collect the customer's VAT number, validate it before creating the Mollie payment, and adjust the amount based on the result.
- If valid: apply reverse charge (0% VAT), store the consultation number for your records
- If invalid: charge VAT at the customer's local rate
### TypeScript / Node.js
```
import Avatcado from "@avatcado/node";
import createMollieClient from "@mollie/api-client";
const avatcado = new Avatcado("avat_live_your_api_key");
const mollie = createMollieClient({ apiKey: "your_mollie_api_key" });
// 1. Validate the customer's VAT number
const { data, error } = await avatcado.vat.validate({
vatNumber: customer.vatNumber,
});
if (error) {
throw new Error(`VAT validation failed: ${error.message}`);
}
// 2. Determine VAT treatment
const applyReverseCharge = data.data.valid;
const amount = applyReverseCharge
? orderTotal // No VAT
: orderTotal * (1 + vatRate); // Add local VAT
// 3. Create Mollie payment
const payment = await mollie.payments.create({
amount: { currency: "EUR", value: amount.toFixed(2) },
description: `Order #${orderId}`,
redirectUrl: "https://your-site.com/order/success",
});
```
### Python
```
from avatcado import Avatcado
from mollie.api.client import Client
avatcado = Avatcado("avat_live_your_api_key")
mollie = Client()
mollie.set_api_key("your_mollie_api_key")
# 1. Validate
result = avatcado.vat.validate(customer_vat_number)
# 2. Determine VAT
if result.data.valid:
amount = order_total # Reverse charge
else:
amount = order_total * (1 + vat_rate)
# 3. Create payment
payment = mollie.payments.create({
"amount": {"currency": "EUR", "value": f"{amount:.2f}"},
"description": f"Order #{order_id}",
"redirectUrl": "https://your-site.com/order/success",
})
```
## What about Swiss, Norwegian, and Australian customers?
If you sell to businesses in Switzerland, Norway, or Australia, you need to validate their tax IDs too. Avatcado handles all of these through the same endpoint. Send a CH, NO, or AU prefixed number and get the same response format. Mollie has no support for any of these.
See the Swiss VAT validation guide, Norwegian VAT validation guide, or Australian GST validation guide for details on each country.
## Revalidating over time
VAT numbers can be deactivated or revoked at any time. For recurring billing (subscriptions), revalidate periodically. Monthly or quarterly is a common cadence.
Avatcado's 25-day cache means repeat lookups for the same number are fast, though cached responses still count toward your monthly quota. See the caching documentation for details.
## Get started
Avatcado's free tier includes 500 validations per month with no credit card required. Same API, same caching, same SDKs as paid plans.
Start validating for free →
Read the API documentation to integrate in under 5 minutes.
## FAQs
Q: Does Mollie validate customer VAT numbers?
A: No. Mollie only validates the merchant's own VAT number during account setup. For customer VAT validation, you need a separate service like Avatcado.
Q: Do I need VAT validation if I only sell B2C through Mollie?
A: No. VAT validation is only relevant for B2B transactions where you need to determine whether the reverse charge mechanism applies. For B2C sales, you generally charge VAT at the customer's local rate, though EU-established sellers below the EUR 10,000 annual cross-border threshold may charge their home country's rate instead.
Q: Can I use Avatcado with Mollie's API?
A: Yes. Avatcado is a standalone REST API. Call it before creating the Mollie payment to determine the correct VAT treatment. The two services are independent.
Q: What happens if I apply reverse charge without validating the VAT number?
A: If the customer's VAT number turns out to be invalid and you zero-rated the transaction, you are liable for the VAT. Since January 2020, a valid VAT number is a material requirement for the 0% rate on intra-Community supplies.
## Sources
- Create Payment API reference (Mollie): https://docs.mollie.com/reference/create-payment
- Payment methods overview (Mollie): https://docs.mollie.com/docs/payment-methods
- What should I do if my VAT number is invalid? (Mollie): https://help.mollie.com/hc/en-us/articles/115001480005-What-should-I-do-if-my-VAT-number-is-invalid
- Council Directive (EU) 2018/1910 (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2018/1910/oj
---
# VAT Validation for Stripe Users: Coverage Gaps
> Stripe validates EU, UK, and AU tax IDs but skips Swiss, Norwegian, and 100+ other types. Learn where Stripe's validation falls short and how Avatcado fills the gaps.
Published: 2026-03-30
Updated: 2026-03-30
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-stripe
Stripe is the most popular payment platform for SaaS businesses. It includes built-in VAT validation, but that validation has significant limitations. This guide covers exactly what Stripe validates, what it doesn't, and when you need a standalone VAT validation API alongside Stripe.
## What Stripe actually validates
Stripe automatically validates tax IDs against government databases for three regions only:
- EU VAT numbers via VIES
- UK VAT numbers via HMRC
- Australian ABNs via ABR
For all other tax ID types (100+ types across 50+ countries), Stripe only does format validation. It checks if the number matches the expected pattern but does not verify it exists in any government database.
From Stripe's own documentation: "If automatic validation isn't available, you must manually verify these IDs."
Stripe Tax applies reverse charge based on format alone, not validation. A correctly formatted but fake VAT number would still trigger reverse charge in Stripe.
## Five gaps in Stripe's VAT validation
### No Swiss validation
Stripe stores CH tax IDs but does not validate them against the BFS UID Register. Format check only. If you sell to Swiss businesses, you cannot rely on Stripe to confirm the number is real. See the Swiss VAT validation guide for how the BFS UID Register works.
### No Norwegian validation
Same story. NO tax IDs get format-checked, not validated against the Bronnoysund Register.
### No standalone API
Validation is tied to Stripe's Customer objects. You cannot just call an endpoint with a VAT number and get a result. You need a Stripe Customer and must attach the tax ID to it through the Tax IDs API first.
### No periodic revalidation
Stripe validates once when the tax ID is added. It does not recheck over time. A VAT number that was valid last year could be revoked today, and Stripe would not know.
### No validation outside billing
If you need to validate a VAT number at signup, in a CRM, in a partner onboarding flow, or anywhere outside a Stripe payment context, Stripe cannot help.
## When to use Avatcado alongside Stripe
- You sell to Swiss or Norwegian businesses (Stripe cannot validate these)
- You need validation at signup before any Stripe interaction
- You need periodic revalidation of existing customer tax IDs
- You use Stripe for payments but need validation in other systems (CRM, onboarding, compliance)
- You want to verify that the number is actually valid, not just correctly formatted
Validate before creating the Stripe customer:
```
import Avatcado from "@avatcado/node";
import Stripe from "stripe";
const avatcado = new Avatcado("avat_live_your_api_key");
const stripe = new Stripe("sk_live_your_stripe_key");
// Validate before creating the Stripe customer
const { data, error } = await avatcado.vat.validate({
vatNumber: customer.vatNumber,
});
if (data?.data.valid) {
// Safe to apply reverse charge in Stripe. Tax IDs are attached
// through the Tax IDs API; the Update Customer API does not
// accept tax_id_data (that parameter is create-only).
await stripe.customers.createTaxId(customerId, {
type: "eu_vat",
value: customer.vatNumber,
});
}
```
## Using both: Avatcado for validation, Stripe for billing
Avatcado handles the validation (real government database check, all 32 countries). Stripe handles the billing (invoicing, tax calculation, payment collection). The two work together: validate with Avatcado first, then pass the validated number to Stripe.
This gives you the best of both: Stripe's billing infrastructure plus Avatcado's validation coverage. For a full integration example, see the SaaS billing guide.
## Get started
Avatcado's free tier includes 500 validations per month with no credit card required. Add real government database validation to your Stripe integration in under 5 minutes.
Start validating for free →
Read the API documentation for integration details.
## FAQs
Q: Does Stripe validate Swiss VAT numbers?
A: No. Stripe stores Swiss tax IDs and checks the format, but does not validate them against the BFS UID Register. You need a separate service for real Swiss VAT validation.
Q: Can I use Stripe's tax ID validation as a standalone API?
A: No. Stripe's validation is tied to their Customer and Invoice objects. You need a Stripe Customer and must attach the tax ID to it via the Tax IDs API to trigger validation. Avatcado provides a standalone REST endpoint for any VAT/GST number.
Q: Does Stripe revalidate tax IDs over time?
A: No. Once a tax ID is confirmed as valid or invalid, Stripe does not check it again. VAT numbers can be revoked, so periodic revalidation is recommended.
Q: I only sell to EU and UK businesses. Do I still need Avatcado?
A: If all your customers are EU or UK and you only need validation within Stripe's billing flow, Stripe's built-in validation may be sufficient. Avatcado adds value when you need validation outside the billing context, need Swiss or Norwegian coverage, or want periodic revalidation.
## Sources
- Customer Tax IDs (Stripe): https://docs.stripe.com/billing/customer/tax-ids
- Collect customer tax IDs with Checkout (Stripe): https://docs.stripe.com/tax/checkout/tax-ids
- Update Customer API reference (Stripe): https://docs.stripe.com/api/customers/update
- Create Tax ID API reference (Stripe): https://docs.stripe.com/api/tax_ids/create
---
# EU VAT Validation for US SaaS Companies
> US SaaS companies selling B2B to EU customers can avoid charging VAT via the reverse charge, but must validate the buyer's VAT number. Here's how.
Published: 2026-03-30
Updated: 2026-03-30
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/eu-vat-validation-us-saas
If you run a SaaS company in the US and have customers in Europe, you need to deal with VAT. The rules are different from US sales tax, and getting them wrong can result in penalties. The good news: for B2B sales, the reverse charge mechanism means you don't need to charge VAT. The bad news: you need to validate your customer's VAT number to use it.
## VAT vs US sales tax
US sales tax is charged at the point of sale by the seller, based on the seller's or buyer's state. EU VAT is charged at each stage of production and distribution. For cross-border B2B services, the buyer self-accounts via reverse charge.
US companies selling to EU businesses don't charge VAT, but they must verify the buyer is a real business. The only reliable way to do that: validate their VAT number against a government database.
## B2B vs B2C: why it matters
- B2B (buyer has a valid VAT number): reverse charge applies, you invoice without VAT, buyer self-accounts
- B2C (buyer is a consumer or has no valid VAT number): you must charge VAT at the buyer's local rate, register for VAT (Non-Union OSS), and remit it
The only way to distinguish B2B from B2C reliably: validate the VAT number. Only businesses can have one. Some customers will claim to be businesses to avoid VAT. Validation prevents this fraud.
For a deeper explanation of the reverse charge mechanism and its legal requirements, see our dedicated guide.
## How to validate EU VAT numbers from the US
VIES is the EU's official validation service. It's free and accessible from anywhere, including the US. However, integrating directly with VIES means dealing with a SOAP/XML API, per-country downtime, and no UK support.
Avatcado wraps VIES (and HMRC for UK, plus Swiss, Norwegian, and Australian registries) in a single REST API:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
// Customer claims to be an EU business
const { data, error } = await avatcado.vat.validate({
vatNumber: "DE123456789",
});
if (data?.data.valid) {
// Confirmed business: invoice without VAT (reverse charge)
// Store: company name, validation timestamp, consultation number
} else {
// Not a valid business: charge local VAT rate
// You'll need Non-Union OSS registration for this
}
```
## What about the UK, Switzerland, Norway, and Australia?
Post-Brexit, UK VAT numbers are not in VIES. You need HMRC validation separately. Swiss, Norwegian, and Australian businesses have their own tax ID systems.
If you sell to businesses in any of these countries, you may need to validate their tax IDs. Avatcado handles all of them through the same endpoint. One API call, regardless of country.
## Do US companies need an EU VAT number?
- For B2B sales only: No. Reverse charge means the buyer handles VAT.
- For B2C sales to EU consumers: Yes. You must register for VAT via the Non-Union OSS (One Stop Shop) and charge local VAT rates. There is no revenue threshold for digital services.
Having an EU VAT number also allows you to request consultation numbers when validating other EU businesses, which provides timestamped proof of validation for audit purposes.
## Get started
Avatcado's free tier includes 500 validations per month with no credit card required. No EU VAT registration needed to use the API.
Start validating for free →
Read the API documentation or check out the TypeScript integration guide to get started in under 5 minutes.
## FAQs
Q: Do US companies need to charge EU VAT?
A: It depends on the customer. For B2B sales where the buyer provides a valid VAT number, no. Reverse charge applies. For B2C sales to EU consumers, yes. You must register for VAT and charge the local rate.
Q: Can I access VIES from the US?
A: Yes. VIES is publicly accessible from anywhere. Avatcado also works from any location and wraps VIES in a modern REST API.
Q: What if my EU customer doesn't have a VAT number?
A: Treat the sale as B2C. Charge VAT at the customer's local rate. You'll need a VAT registration (Non-Union OSS) to do this.
Q: Do I need a separate integration for UK customers?
A: If you integrate directly, yes. HMRC uses a different API from VIES. If you use Avatcado, no. The same endpoint handles both EU and UK validation automatically.
## Sources
- OSS non-Union scheme registration (European Commission): https://vat-one-stop-shop.ec.europa.eu/one-stop-shop/register-oss_en
- VIES on the Web (European Commission): https://ec.europa.eu/taxation_customs/vies/
- Check a UK VAT number API (HMRC): https://developer.service.hmrc.gov.uk/api-documentation/docs/api/service/vat-registered-companies-api
- Council Directive 2006/112/EC (VAT Directive) (EUR-Lex): https://eur-lex.europa.eu/eli/dir/2006/112/oj
---
# VAT Validation for Adyen Merchants
> Adyen does not offer customer VAT validation. Learn how to add VAT number verification to your Adyen checkout, handle errors, batch-validate at scale, and cover marketplace sub-merchants.
Published: 2026-03-30
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-adyen
Adyen is one of Europe's largest payment platforms, serving enterprise merchants across the globe. If you use Adyen for B2B payments, you need to validate customer VAT numbers for reverse charge compliance. Adyen does not provide this.
## What Adyen does and doesn't do for VAT
Adyen processes payments. It does not validate customer tax IDs, calculate VAT, or automate the reverse charge mechanism.
- Adyen's Legal Entity Management API handles VAT numbers for sub-merchant onboarding (KYC), but this is not available for validating your end customers' VAT numbers
- Adyen's tax-free shopping feature (partnership with Global Blue) is for in-store retail VAT refunds, not B2B VAT validation
- Adyen's documentation has no mention of VIES integration, customer VAT validation APIs, or reverse charge automation for merchants
## Adding VAT validation to your Adyen integration
Validate the customer's VAT number before you create the Adyen payment or checkout session. Use the result to determine VAT treatment (reverse charge or charge VAT), then store the result for audit purposes before the payment session is created:
```
curl "https://api.avatcado.com/v1/validate?vat_number=DE123456789" \
-H "Authorization: Bearer avat_live_your_api_key"
```
```
{
"data": {
"valid": true,
"vat_number": "DE123456789",
"country_code": "DE",
"company": {
"name": "EXAMPLE GMBH",
"address": "MUSTERSTRASSE 1, 10115 BERLIN"
},
"requested_at": "2026-08-07T10:30:00.000Z"
},
"meta": {
"request_id": "9f6a1c2e-4b3d-4a8f-9c7e-2d5b8e1f0a63",
"request_duration_ms": 842,
"source_status": "live"
}
}
```
Or with the TypeScript SDK, wired into the step before you build the Adyen payment request:
```
import Avatcado from "@avatcado/node";
const avatcado = new Avatcado("avat_live_your_api_key");
async function determineVatTreatment(customer: { vatNumber?: string; country: string }, sellerCountry: string) {
if (!customer.vatNumber) {
return { chargeVat: true, vatRate: getVatRate(customer.country) };
}
const { data, error } = await avatcado.vat.validate({ vatNumber: customer.vatNumber });
if (error || !data.data.valid || customer.country === sellerCountry) {
// Invalid, unverifiable, or domestic: charge VAT
return { chargeVat: true, vatRate: getVatRate(customer.country) };
}
// Valid, cross-border, B2B: apply the reverse charge before creating the Adyen session
return { chargeVat: false, reverseCharge: true, companyName: data.data.company?.name };
}
```
Create the Adyen payment or checkout session with the resulting vatRate (or zero-rated amount) already applied. Adyen has no concept of a VAT treatment decision, so this logic has to live in your own backend, ahead of the Adyen call.
## Handling errors and non-200 responses
- 422 invalid_vat_format: The VAT number failed format or checksum validation before Avatcado even called the upstream registry. Surface this to the customer at checkout so they can correct a typo before you attempt the Adyen payment.
- 503 upstream_unavailable: VIES, HMRC, or a national registry is temporarily down. Avatcado serves a cached, stale result when one exists (meta.stale: true). With no cached result, fall back to charging VAT conservatively rather than blocking the checkout, and flag the order for a manual revalidation.
- 429 rate_limit_exceeded: Rare at typical Adyen merchant volumes given the tier limits below, but if you validate on every cart update rather than once at checkout, debounce the calls or validate only on the final submit.
## Adyen for Platforms and marketplaces
If you run Adyen for Platforms, you have two distinct VAT validation needs: your sub-merchants' own VAT numbers (for onboarding and KYC, which Adyen's Legal Entity Management API already covers) and your sub-merchants' end customers' VAT numbers (for reverse charge on individual transactions, which Adyen does not cover at all). Avatcado handles the second case regardless of how many sub-merchants sit on your platform, since it validates one VAT number per call independent of any Adyen account structure.
## Batch validation for high-volume Adyen merchants
If you are backfilling VAT validation for existing customers, or reconciling a batch of orders at the end of a billing cycle, use the batch endpoint instead of looping single requests (Pro and Business plans, up to 50 numbers per request):
```
curl -X POST "https://api.avatcado.com/v1/validate/batch" \
-H "Authorization: Bearer avat_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"vat_numbers": [
"DE123456789",
"FR82542065479",
"NL123456789B01"
]
}'
```
Each item in the response has its own data (or error) and meta block, so a single malformed or unresponsive VAT number in the batch does not block validation of the others.
## Enterprise reliability
Adyen merchants typically process high volumes. VAT validation needs to be reliable at scale.
- Caching: Avatcado caches results for 25 days. Repeat lookups for the same number resolve from cache in milliseconds, with no upstream round trip.
- Retries: Automatic retries with backoff when VIES is down, plus stale cache fallback for HMRC and the national registries.
- Stale fallback: If a country's tax service is offline and no fresh result is available, Avatcado returns the last known result with a stale indicator so your checkout flow doesn't break.
- Rate limits: Scale with your plan. 120 requests per minute on the Business tier.
- Batch validation: Process up to 50 numbers in a single request on Pro and Business plans.
See the pricing page for plan details and rate limits.
## Limitations
- No Adyen-native integration. Avatcado is a standalone REST API with no Adyen app, marketplace listing, or webhook that fires on Adyen events. You call it from your own backend, on your own schedule, before you talk to Adyen.
- You own the VAT decision logic. Adyen has no field or setting for "reverse charge applied." The zero-rating decision and any resulting invoice wording are entirely your responsibility to implement and store.
- No revalidation trigger. Adyen will not notify you if a previously valid customer VAT number is later deregistered. For recurring or subscription merchants, schedule periodic revalidation yourself, separate from the Adyen payment flow.
## Get started
Avatcado's free tier includes 500 validations per month with no credit card required. Add VAT validation to your Adyen integration in under 5 minutes.
Start validating for free →
Read the API documentation for integration details.
## FAQs
Q: Does Adyen validate customer VAT numbers?
A: No. Adyen collects VAT numbers for sub-merchant KYC purposes in their Platforms product, but does not offer customer VAT validation for merchants.
Q: Can I use Avatcado with Adyen?
A: Yes. Avatcado is a standalone API. Validate the customer's VAT number before creating the Adyen payment session. The two services are independent.
Q: What about high-volume validation?
A: Avatcado's Business plan supports 50,000 validations per month with 120 requests per minute burst rate. For higher volumes, contact us for a custom plan.
## Sources
- Legal Entity Management API: Create a legal entity (Adyen): https://docs.adyen.com/api-explorer/legalentity/latest/post/legalEntities
- Tax-free shopping with secure card capture (Adyen): https://docs.adyen.com/point-of-sale/shopper-recognition/tax-free-shopping
- Global Blue technology partner (Adyen): https://www.adyen.com/partners/globalblue
---
# Async VAT Validation: Webhooks and Batch Processing
> Learn how to validate VAT numbers asynchronously using webhooks and batch processing. Covers checkout latency, bulk revalidation, VIES downtime resilience, and webhook signature verification.
Published: 2026-03-30
Updated: 2026-03-30
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/async-vat-validation-webhooks
Sync VAT validation works for most use cases, but it breaks down in three scenarios: high-latency checkout flows, bulk revalidation of existing customers, and VIES downtime. Async validation solves all three. Submit the request, get an immediate 202, and receive the result via webhook.
## When sync validation is not enough
### Checkout latency
In our monitoring, VIES takes 1 to 3 seconds per lookup depending on the member state, with some countries consistently at the slow end. In a checkout flow, that delay is visible to the customer. Async validation lets you accept the order immediately and process the VAT check in the background.
### Bulk revalidation
If you have 500+ B2B customers, revalidating their VAT numbers quarterly means 500+ sequential API calls with rate limiting delays. With async batch validation, you submit all numbers in a single request (up to 200 for Pro, 1,000 for Business) and receive one webhook when all results are ready.
### VIES downtime resilience
When a country's VIES service is down, sync validation fails with a 503 error. Async validation moves that failure handling out of your request path: Avatcado retries VIES briefly during the lookup and falls back to a recent cached result when one exists. If the registry is down and there is no cached result, you receive a validation.failed webhook and the validation is refunded to your quota, so you can resubmit once the service recovers.
## How async validation works
### Single async validation
POST one number to /v1/validate/async, get a 202 with a request_id, and receive a validation.completed or validation.failed webhook when processing finishes.
### Batch async validation
POST an array of numbers to /v1/validate/async/batch. Avatcado validates each format immediately. Invalid formats are rejected in the 202 response (never queued). Valid items are processed in the background and a single batch.completed webhook is delivered when all items finish.
### Example: submitting a batch
```
const response = await fetch("https://api.avatcado.com/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer avat_live_your_api_key",
},
body: JSON.stringify({
vat_numbers: ["DE123456789", "NL987654321B01", "FR12345678901"],
cache: true,
}),
});
const { data } = await response.json();
// data.batch_id: "550e8400-..."
// data.accepted: 3
// data.rejected: []
// data.status: "pending"
// Results arrive via webhook when processing completes
```
## Handling webhook deliveries
Every webhook is signed with HMAC-SHA256 using your signing secret. Always verify the signature before processing the payload.
### Webhook handler example
```
import { createHmac } from "crypto";
// Express / Node.js example
app.post("/webhooks/avatcado", (req, res) => {
const signature = req.headers["x-avatcado-signature"];
const timestamp = req.headers["x-avatcado-timestamp"];
const body = req.body; // raw string
// Verify signature
const expected = createHmac("sha256", process.env.AVATCADO_WEBHOOK_SECRET)
.update(`${timestamp}.${body}`)
.digest("hex");
if (signature !== `sha256=${expected}`) {
return res.status(401).send("Invalid signature");
}
const event = JSON.parse(body);
switch (event.event) {
case "validation.completed":
// event.data contains the validation result
updateCustomerVatStatus(event.data.vat_number, event.data.valid);
break;
case "batch.completed":
// event.data.results contains all items
for (const item of event.data.results) {
if ("data" in item) {
updateCustomerVatStatus(item.data.vat_number, item.data.valid);
}
}
break;
case "validation.failed":
// event.error has the failure reason (event.data contains the vat_number)
flagForManualReview(event.data.vat_number, event.error);
break;
}
res.status(200).send("OK");
});
```
## Use case: quarterly customer revalidation
Companies with hundreds of B2B customers should revalidate VAT numbers periodically. VAT registrations can be revoked, businesses can close, and numbers can become invalid at any time. Quarterly revalidation is a common practice for compliance.
With async batch validation, the process is simple:
- Query your database for all active customer VAT numbers
- Submit them in a single batch request (up to 200 or 1,000 depending on your plan)
- Receive a batch.completed webhook with all results
- Update your records and flag any newly invalid numbers for review
```
// Quarterly revalidation script
const customers = await db.query("SELECT vat_number FROM customers WHERE active = true");
const vatNumbers = customers.map((c) => c.vat_number);
const response = await fetch("https://api.avatcado.com/v1/validate/async/batch", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.AVATCADO_API_KEY}`,
},
body: JSON.stringify({ vat_numbers: vatNumbers }),
});
const { data } = await response.json();
console.log(`Batch ${data.batch_id}: ${data.accepted} accepted, ${data.rejected.length} rejected`);
// Results will arrive via webhook
```
## Get started
Async validation and webhooks are available on Pro (starting at €29/month) and Business plans.
- Create a free account and upgrade to Pro
- Configure your webhook URL in the dashboard
- Read the webhook documentation for signature verification and event payload details
- Read the async validation documentation for endpoint details and error handling
## FAQs
Q: What happens if VIES is down when I submit an async request?
A: If a cached result exists for the number, Avatcado serves it from cache (marked stale if it is past the TTL) and delivers it via webhook as usual. If the upstream registry is down and no cached result exists, the request fails fast: you receive a validation.failed webhook and the validation is refunded to your quota, so you can resubmit once the registry recovers.
Q: Do async validations count against my monthly quota?
A: Yes. Each VAT number is counted once when the request is accepted (the 202 response), not when the result is delivered.
Q: Can I mix sync and async validation?
A: Yes. The sync endpoints (GET /v1/validate, POST /v1/validate/batch) continue to work as before. Use sync for real-time validation and async for bulk processing or when you want resilience against downtime.
Q: What is the maximum batch size for async?
A: Pro: 200 items per batch. Business: 1,000 items per batch.
## Sources
- Avatcado async validation documentation (Avatcado): https://docs.avatcado.com/async-validation
- Avatcado webhooks documentation (Avatcado): https://docs.avatcado.com/webhooks
- VIES on-the-Web (European Commission): https://ec.europa.eu/taxation_customs/vies/
---
# Validate VAT Numbers in Your CRM
> How to add real-time VAT number validation to CRM account creation and lead qualification workflows. Covers Salesforce, HubSpot, and no-code automation tools.
Published: 2026-04-03
Updated: 2026-04-03
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-crm
Every major CRM stores VAT numbers as plain text. Out of the box, Salesforce, HubSpot, Pipedrive, and Zoho do not validate VAT numbers against a government registry; whatever a sales rep types into the field is what gets stored. Bad data enters at account creation and propagates into invoices, ERP systems, and compliance records.
This guide covers why you should validate VAT numbers at the point of entry, what a validation returns, and how to connect Avatcado to your CRM.
## Why validate at account creation, not just at checkout
Most VAT validation guides focus on the checkout flow. That makes sense for e-commerce, but in B2B SaaS and services, the VAT number often enters your system weeks or months before the first invoice. A sales rep creates an account record, pastes in a VAT number from an email, and moves on.
If that number is wrong, the problem compounds. Your billing system generates invoices with an invalid VAT number. Your ERP syncs the bad data. You apply the reverse charge to a transaction that does not qualify. When an auditor checks, you have no proof the number was ever valid.
Validating at account creation catches these issues before they spread. It also gives your sales team immediate feedback: if the number is invalid, they can ask the prospect for the correct one while they are still in conversation.
## What validation returns
A VAT validation lookup does more than return valid or invalid. When a number is active in the government registry, the response includes the country code, the registration status, and, where the registry discloses it, the registered legal entity name (Germany and Spain do not disclose name and address via VIES).
This is useful beyond compliance. You can auto-fill the company name field on the CRM record using the official name from the registry. This ensures your records match the legal entity exactly, which matters when you generate invoices or onboard the account into your ERP.
```
// Example response from Avatcado
{
"data": {
"valid": true,
"vat_number": "NL123456789B01",
"country_code": "NL",
"company": {
"name": "Example B.V.",
"address": "Keizersgracht 100, 1015 AA Amsterdam"
},
"requested_at": "2026-03-26T12:00:00.000Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"cached": false
}
}
```
## Integration patterns
There are three common ways to connect Avatcado to your CRM, depending on your team's technical capabilities and the CRM you use.
### Direct API callout
If you are building a custom CRM integration or internal tool, call the Avatcado REST API directly. Send a GET request with the VAT number, parse the JSON response, and update the record. This works with any language and any CRM that exposes an API for updating records.
See the API documentation for the full endpoint reference.
### Salesforce Apex callout
Salesforce supports HTTP callouts from Apex code or Flow actions. You can trigger a validation when an Account record is created or when a VAT number field is updated, then write the result back to custom fields on the Account.
See the Salesforce VAT validation guide for a step-by-step walkthrough with code examples.
### No-code automation via Zapier or Make
If you do not have developers available, you can connect Avatcado to your CRM using Zapier or Make (formerly Integromat). Both tools can call the Avatcado API using their built-in HTTP/webhook modules and write the result back to CRM records without code.
- VAT validation with Zapier
- VAT validation with Make
## Batch validation for existing records
If you already have a CRM with thousands of VAT numbers that were never validated, you do not need to check them one at a time. The Avatcado batch endpoint accepts up to 50 VAT numbers per request on Pro and Business plans.
Export your CRM records, group the VAT numbers into batches of 50, and send them to the batch endpoint. Each result includes the validation status and registered company name, so you can update your CRM records in bulk.
For very large backlogs, the async batch endpoint queues requests and delivers results via webhook. See the async validation guide for details.
## Get started
Avatcado's free tier includes 500 validations per month with no credit card required. That is enough to validate every new account as it enters your CRM.
Start validating for free →
Read the API documentation for the full endpoint reference and SDKs.
## FAQs
Q: Does my CRM validate VAT numbers natively?
A: Salesforce, HubSpot, and Pipedrive all store tax ID or VAT number as a plain text field with no verification; out of the box, none of them validate VAT numbers against a government registry. You need to call a dedicated validation API and write the result back.
Q: What does validation return beyond valid or invalid?
A: A successful lookup returns the registered legal entity name, country code, and active status from the government registry. This is useful for auto-filling company name fields and confirming you are dealing with the correct legal entity.
Q: Can I validate VAT numbers in bulk for existing CRM records?
A: Yes. The Avatcado batch endpoint accepts up to 50 VAT numbers per request on Pro and Business plans. For large backlogs, you can page through your CRM records and process them in batches.
## Sources
- Invoking Callouts Using Apex (Salesforce): https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts.htm
- Send webhooks in Zaps (Zapier): https://help.zapier.com/hc/en-us/articles/8496326446989-Send-webhooks-in-Zaps
- Check a VAT number (VIES) (European Union): https://europa.eu/youreurope/business/taxation/vat/check-vat-number-vies/index_en.htm
---
# Salesforce VAT Validation
> How to validate VAT numbers inside Salesforce using Apex HTTP callouts. Covers Named Credentials, the Avatcado REST API, and writing the validated company name back to Account records.
Published: 2026-04-03
Updated: 2026-04-03
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-salesforce
Salesforce has no standard VAT number field; teams store VAT numbers in custom text fields on Account records, and nothing validates them. Sales reps enter whatever they have, and the data goes unverified into invoices, opportunity records, and ERP syncs.
This guide shows how to validate VAT numbers inside Salesforce using Apex HTTP callouts, Named Credentials, and the Avatcado REST API. You will also learn how to write the validated company name back to Account records.
## The integration pattern
Avatcado is a REST API that accepts a VAT number and returns the validation status, the registered company name, and the country code. In Salesforce, you call it via an Apex HTTP callout. You can trigger the callout from an Apex trigger, a Flow action, or a custom button on the Account page.
The flow is: Account record is created or updated with a VAT number → Apex callout to Avatcado → parse the response → update Account fields with the result.
## Setting up a Named Credential
Named Credentials store the endpoint URL and authentication details so you do not hardcode API keys in Apex. Salesforce injects the credentials automatically when your code references the Named Credential.
To set up a Named Credential for Avatcado:
- Go to Setup → Named Credentials → External Credentials. Create a new External Credential with the authentication protocol set to "Custom".
- Add a Principal. In the principal's authentication parameters, store your API key as a parameter named ApiKey.
- Create a Named Credential that references this External Credential. Set the base URL to https://api.avatcado.com.
- On the Named Credential, add a Custom Header named Authorization with the formula {!'Bearer ' & $Credential.Avatcado.ApiKey}. Uncheck "Generate Authorization Header" and check "Allow Formulas in HTTP Header".
- Create a Permission Set that grants access to the External Credential principal. Assign it to the users or integration user that will run the callout.
## Apex callout example
The following Apex class makes a GET request to the Avatcado validate endpoint and returns a parsed result. It uses the Named Credential so the Authorization header is injected automatically.
```
public class VatValidationService {
public class VatResult {
public Boolean valid;
public String companyName;
public String countryCode;
}
public static VatResult validate(String vatNumber) {
HttpRequest req = new HttpRequest();
req.setEndpoint(
'callout:Avatcado_API/v1/validate?vat_number='
+ EncodingUtil.urlEncode(vatNumber, 'UTF-8')
);
req.setMethod('GET');
req.setHeader('Accept', 'application/json');
Http http = new Http();
HttpResponse res = http.send(req);
VatResult result = new VatResult();
if (res.getStatusCode() == 200) {
Map body = (Map)
JSON.deserializeUntyped(res.getBody());
Map data = (Map)
body.get('data');
result.valid = (Boolean) data.get('valid');
result.countryCode = (String) data.get('country_code');
Map company = (Map)
data.get('company');
if (company != null) {
result.companyName = (String) company.get('name');
}
} else {
result.valid = false;
}
return result;
}
}
```
## Writing the result back to Account
Once you have the validation result, update the Account record with custom fields. A common pattern uses two fields: a checkbox VAT_Validated__c and a text field Registered_Company_Name__c.
You can call the VatValidationService.validate() method from:
- An Apex trigger on Account (after insert or after update on the VAT number field). Be careful: Apex triggers cannot make synchronous callouts directly. Use a @future(callout=true) method or a Queueable to make the callout asynchronously.
- A Flow action. Annotate a wrapper method with @InvocableMethod so Flow can call it. This lets admins add VAT validation to any Flow without writing additional Apex.
- A custom button or Lightning action on the Account page, letting sales reps validate on demand.
## Test mode
Avatcado has a test mode with magic VAT numbers so you can build and test in a Salesforce sandbox without hitting live government APIs. Use an API key prefixed with avat_test_ instead of avat_live_.
Magic numbers like DE111111111 always return valid, and DE000000000 always return invalid. No quota is consumed in test mode. See the documentation for the full list of test numbers.
## Get started
Create a free Avatcado account →
Read the API documentation for the full endpoint reference. For a broader overview of CRM integration patterns, see the CRM validation guide.
## FAQs
Q: Does Salesforce validate VAT numbers natively?
A: No. Salesforce stores VAT and tax ID numbers as plain text on Account records but does not validate them against any government database. Validation requires a callout to an external API.
Q: Do I need to be a Salesforce developer to set this up?
A: The Apex callout approach requires Salesforce development experience. If your org uses Salesforce Flow and you prefer a no-code approach, you can also use Zapier or Make to connect Avatcado to Salesforce without writing Apex.
Q: Can I test this in a Salesforce sandbox?
A: Yes. Use Avatcado's test mode with magic VAT numbers to build and test without hitting live government APIs. Test keys and test magic numbers are documented on docs.avatcado.com.
## Sources
- Named Credentials as Callout Endpoints (Salesforce): https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_callouts_named_credentials.htm
- Use API Keys in Custom Headers with Named Credentials (Salesforce): https://help.salesforce.com/s/articleView?id=sf.nc_custom_headers_and_api_keys.htm&language=en_US&type=5
- 'Callout from triggers are currently not supported' error (Salesforce): https://help.salesforce.com/s/articleView?id=000386018&language=en_US&type=1
- InvocableMethod Annotation (Salesforce): https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_classes_annotation_InvocableMethod.htm
---
# VAT Validation with Zapier
> How to validate VAT numbers in a Zapier workflow using the Webhooks by Zapier action and the Avatcado REST API. Covers error handling, scheduled revalidation, and limitations. No code required.
Published: 2026-04-03
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-zapier
Zapier does not have a native VAT validation app. You can connect Avatcado to any Zapier workflow using the Webhooks by Zapier action, which calls any REST API with custom headers. No code required.
This guide walks through setting up a Zap that validates a VAT number and uses the result in downstream steps.
## When to use this
Zapier is a good fit when you want to validate VAT numbers as part of an existing automation, without writing code. Common use cases:
- Validate a VAT number when a new HubSpot contact or company is created
- Validate when a Typeform, JotForm, or Google Forms submission includes a VAT number
- Validate when a new row appears in a Google Sheet (e.g., a partner onboarding spreadsheet)
- Validate when a new Salesforce Account is created (if you prefer no-code over Apex)
## Setting up the Zap
The Zap has two parts: a trigger (the event that starts it) and an action (the Avatcado API call). You then add more steps to use the result.
### 1. Choose your trigger
Pick the event that should start the validation. For example, "New Contact" in HubSpot, "New Entry" in Typeform, or "New Spreadsheet Row" in Google Sheets. The trigger must provide a field that contains the VAT number.
### 2. Add the Webhooks by Zapier action
Add a new action step. Search for "Webhooks by Zapier" and select "Custom Request" as the action event. This gives you full control over the HTTP request.
- Method: GET
- URL: https://api.avatcado.com/v1/validate?vat_number={{vat_number_field}} (map the VAT number field from your trigger)
- Headers: add a header with key Authorization and value Bearer avat_live_your_api_key
When you test the step, Zapier sends the request and displays the parsed JSON response. Each field in the response (like data__valid and data__company__name) becomes available as a variable in subsequent steps. A real Avatcado response looks like this before Zapier flattens it:
```
{
"data": {
"valid": true,
"vat_number": "NL123456789B01",
"country_code": "NL",
"company": {
"name": "EXAMPLE B.V.",
"address": "KEIZERSGRACHT 100, 1015 AA AMSTERDAM"
},
"requested_at": "2026-08-07T10:30:00.000Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"cached": false
}
}
```
Zapier flattens nested JSON with double underscores, so data.company.name becomes data__company__name in the step's output, and that is the field name you reference in every step after the webhook.
## Using the result
After the webhook step, you can use the validation result in downstream actions:
- Update a CRM record: add a HubSpot or Salesforce update step that writes the validated company name (data__company__name) back to the contact or account record.
- Filter the Zap: add a Filter step that continues only if data__valid is true. This lets you skip downstream steps for invalid numbers.
- Send a notification: if the number is invalid, send a Slack message or email to the responsible sales rep so they can follow up.
## Handling errors and non-200 responses
When the Avatcado API returns a 4xx or 5xx status, the Webhooks by Zapier step errors and halts the Zap, so a Filter step never sees those responses. Use a Filter only to branch on the 200 response body (for example, data__valid true or false). For the error statuses below, attach Zapier's custom error handling to the webhook step, or rely on autoreplay for transient failures:
- 422 invalid_vat_format: The VAT number failed format or checksum validation. Route this to a path that flags the record for manual correction rather than retrying.
- 503 upstream_unavailable: The upstream registry (VIES, HMRC, or a national registry) is temporarily down. Avatcado returns a cached, stale result when one is available (check meta__stale). If there is no cached result, use Zapier's built-in retry for the step, or add an error handler to the step.
- 429 rate_limit_exceeded: If a Zap runs on every CRM update and you exceed your plan's rate limit during a bulk import, add a Delay step between validations or switch that import to the batch endpoint instead of running it through Zapier.
## Revalidating existing records on a schedule
VAT registrations can lapse after the initial validation. For subscription businesses, set up a second Zap on a Schedule by Zapier trigger (for example, monthly) that searches for CRM records due for revalidation, loops through them with a Looping by Zapier action, and repeats the same Webhooks by Zapier call for each one. Since Avatcado caches results for 25 days, a monthly cadence mostly re-checks against fresh upstream data rather than the cache, which is the point: catching numbers that were deregistered since the last check.
## Limitations
- Not built for bulk backlogs. Zapier runs each record as its own Zap run and bills a task per action step, loops cap at 500 iterations, and the platform is designed for event-driven automation, not bulk data processing. If you need to validate thousands of existing VAT numbers in your CRM, the Avatcado batch endpoint is a better fit. It accepts up to 50 numbers per request on Pro and Business plans.
- Webhooks by Zapier requires a paid plan. The action used in this guide is not included in Zapier's free tier.
- Looping consumes tasks quickly. The Looping step itself is free; each action step inside the loop uses one task per iteration against your Zapier plan's task quota, so validating large lists this way can get expensive compared to a single batch API call.
## Get started
Create a free Avatcado account →
Read the API documentation for the full endpoint reference. For a broader overview of CRM integration patterns, see the CRM validation guide.
## FAQs
Q: Does Zapier have a native VAT validation app?
A: No. You connect Avatcado to Zapier using the Webhooks by Zapier action, which can call any REST API with a custom Authorization header.
Q: Can I use Zapier to validate VAT numbers in HubSpot?
A: Yes. Set your trigger to new or updated HubSpot contact or company, add a Webhooks by Zapier step to call the Avatcado API, then add a HubSpot update step to write the validated company name back to the record.
Q: Is Zapier suitable for validating large numbers of VAT records?
A: Zapier runs each record as its own Zap run and bills a task per action step, so it is not designed for bulk operations. For validating thousands of existing records, use the Avatcado batch endpoint directly via the API.
## Sources
- Send webhooks in Zaps (Zapier): https://help.zapier.com/hc/en-us/articles/8496326446989-Send-webhooks-in-Zaps
- How to get started with Webhooks by Zapier (Zapier): https://help.zapier.com/hc/en-us/articles/8496083355661-How-to-get-started-with-Webhooks-by-Zapier
- Set up custom error handling (Zapier): https://help.zapier.com/hc/en-us/articles/22495436062605-Set-up-custom-error-handling
- Understanding Looping by Zapier (Zapier): https://help.zapier.com/hc/en-us/articles/42969233918477-Understanding-Looping-by-Zapier
---
# VAT Validation with Make
> How to validate VAT numbers in a Make scenario using the HTTP module and the Avatcado REST API. Covers error handling, scheduled revalidation, and limitations. No code required.
Published: 2026-04-03
Updated: 2026-08-07
Reviewed: 2026-08-13
Canonical URL: https://www.avatcado.com/guides/vat-validation-make
Make (formerly Integromat) does not have a native VAT validation module. You can call Avatcado from any Make scenario using the HTTP module, which supports custom headers and JSON response parsing. No code required.
This guide walks through setting up a Make scenario that validates a VAT number and routes the result to different actions.
## When to use this
Make is a good fit when you want to validate VAT numbers as part of a multi-step automation. Common use cases:
- Validate a VAT number when a new CRM record is created (HubSpot, Salesforce, Pipedrive)
- Validate when a form submission comes in (Typeform, Google Forms, Tally)
- Validate as part of a customer or partner onboarding scenario
- Periodic revalidation of existing records using a scheduled scenario
## Setting up the HTTP module
Add an HTTP module to your scenario and configure it to call the Avatcado API.
### 1. Add the module
In your scenario, add a new module. Search for "HTTP" and select "Make a request" as the action.
### 2. Configure the request
- URL: https://api.avatcado.com/v1/validate?vat_number={{vat_number}} (map the VAT number from a previous module)
- Method: GET
- Headers: add a header with name Authorization and value Bearer avat_live_your_api_key
- Parse response: set to Yes so Make exposes the JSON response fields as variables in subsequent modules
### 3. Test the module
Run the module once with a real or test VAT number. Make will display the parsed response. You should see data.valid, data.company.name, and data.country_code as available fields. A real Avatcado response looks like this:
```
{
"data": {
"valid": true,
"vat_number": "NL123456789B01",
"country_code": "NL",
"company": {
"name": "EXAMPLE B.V.",
"address": "KEIZERSGRACHT 100, 1015 AA AMSTERDAM"
},
"requested_at": "2026-08-07T10:30:00.000Z"
},
"meta": {
"request_id": "550e8400-e29b-41d4-a716-446655440000",
"cached": false
}
}
```
Every field under data becomes a mappable variable in later modules, referenced as {{2.data.valid}} or {{2.data.company.name}} (where 2 is the HTTP module's position in the scenario).
## Using the result
After the HTTP module, use a Router to branch the scenario based on the validation result.
- Valid branch: add a filter that checks data.valid equals true. In this branch, update the CRM record with the registered company name from data.company.name.
- Invalid branch: add a filter that checks data.valid equals false. Send a notification (Slack, email, or a task in your project management tool) so someone can follow up.
You can also skip the Router and use a single Filter module if you only need to act on one outcome (e.g., only notify on invalid numbers).
## Handling errors and non-200 responses
The HTTP module in Make does not automatically fail a scenario on a non-200 status code unless you check "Evaluate all states as errors (except for 2xx and 3xx)" in the module's advanced settings. For VAT validation, decide deliberately: a 422 means the VAT number is malformed (checksum or format failure), and a 503 means the upstream registry (VIES, HMRC, or a national registry) is temporarily unavailable.
- 422 invalid_vat_format: Treat this as a user input error. Route it to a branch that flags the record for manual review instead of retrying automatically.
- 503 upstream_unavailable: Avatcado already serves a cached, stale result when one exists (check meta.stale in the response). If there is no cached result, add a retry with Make's built-in error handler and a short delay before failing the run.
- 429 rate_limit_exceeded: If you are validating many records in a loop, add a Sleep module between iterations or reduce the scenario's execution frequency to stay within your plan's rate limit.
## Scheduled revalidation
VAT registrations can lapse, so periodic revalidation of existing CRM records is common practice for subscription businesses. Build a second scenario on a schedule trigger (for example, monthly) that:
- Retrieves a batch of existing accounts from your CRM with a Search Records module
- Uses an Iterator to loop over each account's stored VAT number
- Calls the same HTTP module pattern described above for each one
- Writes the updated validation result and timestamp back to the record
Because Avatcado caches results for 25 days, a monthly revalidation scenario mostly hits fresh upstream data rather than the cache, which is the point: you want to catch VAT numbers that were deregistered since the last check.
## Limitations
- Not built for bulk backlogs. Make supports iterators and aggregators for processing multiple items within a single scenario run, but each item still makes a separate HTTP request and counts against your Make operations quota. For validating thousands of existing records in one pass, the Avatcado batch endpoint (up to 50 numbers per request) is far more efficient, and the async endpoint handles even larger volumes via webhooks.
- No native retry/backoff. Unlike a purpose-built SDK, Make's HTTP module does not retry failed requests automatically. You need to configure Make's error handler directives (Resume, Ignore, Rollback, Break, Commit) explicitly if you want resilience against transient upstream failures.
- Field mapping is manual. Every response field you want to use downstream must be mapped explicitly in each subsequent module. There is no schema validation to catch a typo in a mapped field name until the scenario runs and the field resolves to empty.
## Get started
Create a free Avatcado account →
Read the API documentation for the full endpoint reference. For a broader overview of CRM integration patterns, see the CRM validation guide.
## FAQs
Q: Does Make have a native VAT validation module?
A: No. You call Avatcado from Make using the HTTP module, which supports custom headers and JSON response parsing.
Q: How do I route a Make scenario based on whether a VAT number is valid?
A: After the HTTP module, add a Router or Filter module that checks the value of the data.valid field in the parsed response. Route valid numbers to one path and invalid numbers to another.
Q: Is Make suitable for validating large numbers of VAT records?
A: Make supports iterators and aggregators for batch-style processing within a scenario, which gives it more flexibility than Zapier for larger volumes. For very large backlogs, the Avatcado batch endpoint is more efficient.
## Sources
- HTTP app (Make a request module) (Make): https://apps.make.com/http-legacy
- Router (Make): https://help.make.com/router
- Resume error handler (Make): https://help.make.com/resume-error-handler
- Make pricing (operations) (Make): https://www.make.com/en/pricing