You're staring at a checkout form or supplier onboarding screen with an EORI field and a blunt question: can this number be trusted, and if not, who do you call next? That's the part most tutorials skip. In production, EORI number validation is less about one magical lookup and more about getting three things right in order, routing the prefix to the right authority, checking the format locally, then confirming status against the live registry.
Table of Contents
- What an EORI Number Is and Why Validation Matters
- Country Prefixes and the EORI Regex Patterns That Work
- Building the Format Check in Node.js and Python
- Client-Side vs Server-Side Validation Tradeoffs
- Routing the Lookups to the Right Authority
- Edge Cases, Failures, and Consent-Gated Responses
- Wiring EORI Checks Into Checkout, Billing, and KYC
What an EORI Number Is and Why Validation Matters
An EORI number is the Economic Operators Registration and Identification number used in customs workflows. If your product handles import or export declarations, supplier screening, or tax treatment that touches customs, this identifier belongs in your data model, not in a spreadsheet someone checks once a quarter. The European Commission formalized the system under the EU customs framework, and it became mandatory in 2009 through Commission Regulation (EC) No 312/2009, which is why validation is now a baseline step in cross-border trade workflows for EU activity, with the official EORI validation service acting as the source of truth for status checks. European Commission EORI guidance
A VAT number and an EORI number are not the same thing. They live in different registries, serve different purposes, and are governed by different authorities. In practice, that means a VAT match tells you something about tax registration, while an EORI match tells you whether a business can be recognized in customs processes. If your checkout or onboarding flow blends those two, you'll eventually approve a record that looks plausible but fails in customs.

Practical rule: treat the field as a customs identifier first, not just a label next to VAT. If your flow can't explain which authority owns the number, it's not ready to validate it.
The clean mental model is simple. Normalize the input, check the structure, route by prefix, then ask the official registry whether the number exists and is active. The European Commission guidance also notes that an EORI number can be deleted from the database only 10 years after its expiry date, which means old records can linger even when the underlying status has changed. That's another reason format validation alone is never enough. European Commission EORI guidance
For a plain-language glossary entry you can hand to product or support teams, use the internal reference at TaxID's EORI number glossary.
Country Prefixes and the EORI Regex Patterns That Work
The first check I run in code is the prefix, because that determines which authority can answer the lookup at all. Official guidance splits validation by jurisdiction, and HMRC's checker only validates GB numbers, while XI and EU-27 numbers need the European Commission route. That routing detail is not a footnote, it decides whether the request reaches a real registry or dies before it starts. HMRC EORI checking guidance, UK checker guidance
A common local pattern is the one many teams end up using in production, two letters followed by up to 15 alphanumeric characters, after normalizing to uppercase and removing spaces. The regex is a guardrail, not proof. A syntactically valid string can still be unregistered, inactive, or revoked, so local acceptance should only mean “worth a live lookup,” never “approved.” For teams that keep mixing this up with VAT formatting rules, the VAT number format glossary is a useful sanity check.
EORI prefix rules for major markets
| Prefix | Character set after prefix | Example | Validation authority |
|---|---|---|---|
| GB | Digits only in the common UK format | GB123456789000 | HMRC |
| XI | Country-specific EU-style structure | XI123456789000 | European Commission route in the UK context |
| DE | Country-specific EU-style structure | DE1234567890123 | European Commission route |
| FR | Country-specific EU-style structure | FR12345678901234 | European Commission route |
| NL | Country-specific EU-style structure | NL123456789 | European Commission route |
| IT | Country-specific EU-style structure | IT123456789012345 | European Commission route |
| ES | Country-specific EU-style structure | ESB12345678 | European Commission route |
| PL | Country-specific EU-style structure | PL1234567890 | European Commission route |
| IE | Country-specific EU-style structure | IE1234567A | European Commission route |
The main problem is not the regex, it is jurisdictional routing. After Brexit, GB and XI stopped behaving like one validation surface, and any system that treats them the same will misroute lookups sooner or later. For format-only utilities, I keep the pattern narrow and let the authority-specific lookup decide status, because the registry, not the regex, is the judge.
Implementation note: if your regex succeeds, that means the string is shaped plausibly. It does not mean a customs authority will accept it.
Building the Format Check in Node.js and Python
I keep the local validator boring on purpose. It should normalize input, reject obvious junk fast, and return a structured result the rest of the pipeline can trust. Don't make it async, and don't let it talk to the network, because its job is to stop bad input before you spend time on a remote lookup. For broader API integration patterns, the TaxID API integration guide is a useful reference point for how teams usually structure this layer.
Node.js example
const EORI_PATTERN = /^[A-Z]{2}[A-Z0-9]{1,15}$/;
function validateEoriFormat(input) {
const normalized = String(input || "")
.toUpperCase()
.replace(/\s+/g, "");
if (!EORI_PATTERN.test(normalized)) {
return {
valid: false,
reason: "format_invalid",
normalized,
};
}
return {
valid: true,
reason: null,
normalized,
prefix: normalized.slice(0, 2),
body: normalized.slice(2),
};
}
Python example
import re
EORI_PATTERN = re.compile(r"^[A-Z]{2}[A-Z0-9]{1,15}$")
def validate_eori_format(value):
normalized = str(value or "").upper().replace(" ", "")
if not EORI_PATTERN.match(normalized):
return {
"valid": False,
"reason": "format_invalid",
"normalized": normalized,
}
return {
"valid": True,
"reason": None,
"normalized": normalized,
"prefix": normalized[:2],
"body": normalized[2:],
}
Both snippets intentionally return the same contract. That makes the downstream code easy to share between a checkout controller, a serverless function, and a batch onboarding job. If the validator returns format_invalid, the API layer can short-circuit immediately, while a pass still leaves room for routing and authoritative status checks.
Practical rule: keep the format checker synchronous and local. If you need a network call to decide whether a string is even worth checking, the abstraction is too heavy.
The one thing I'd make configurable is the prefix table, not the regex itself. When a new market appears, you want a constant or map entry, not a rewrite in three places and a bug report from finance two weeks later. That is the difference between a utility and a maintenance burden.
Client-Side vs Server-Side Validation Tradeoffs
A buyer pastes an EORI into a checkout form, the field turns red, and the cart page never leaves the browser. That is client-side validation doing its job. It can uppercase the input, strip spaces, and catch obvious typing errors before the user submits, but it cannot tell you whether the number is registered, revoked, or even routed to the right authority. The moment a UI check starts being treated like proof, the workflow has already drifted into a compliance risk.
Server-side validation carries the opposite trade-off. It reaches the live registry, so it can return a meaningful result, but it adds latency, depends on an external service, and can fail during the exact step where finance or logistics wants a clean answer. If you skip it, fabricated numbers and stale registrations can slip into checkout and supplier onboarding. If you skip the client layer, every typo becomes a remote call, and users feel that delay during customs lookup.

The production pattern that holds up is using both. The browser handles the fast-fail guardrail, then the server performs the authoritative step after debounce or on form submission. Users get immediate feedback, while the backend keeps the final say on customs status. It also stops your API from spending cycles on strings that never had a chance of passing.
Short version: client-side validation protects the user experience, server-side validation protects the business decision.
The failure modes are different, and you need both in view. An EORI can pass a regex and still be unregistered, inactive, or revoked. A browser-only check cannot catch that. A server-only check forces every typo through the network and makes service instability feel worse than it already is. The practical split is simple, the frontend verifies format, the backend verifies status.
Routing the Lookups to the Right Authority
Most implementations break here because they assume there's one universal checker behind the scenes. There isn't. Post-Brexit, the routing decision comes first. GB goes to HMRC, while XI and EU-27 numbers go through the European Commission route or the relevant national customs authority, depending on how your system is wired. HMRC checker guidance, European Commission EORI guidance
A practical decision tree is simple enough to hard-code and strict enough to prevent accidental misuse.
- GB prefix: send to HMRC's checker.
- XI prefix: send to the EU route, not the GB checker.
- EU-27 prefix: send to the European Commission or the relevant national customs authority.
- Unknown prefix: reject locally and don't call any registry.
The edge case worth memorizing is Northern Ireland. XI is not GB, and if you route it to the UK checker, you'll get a false negative even when the number is valid. That's a jurisdiction bug, not a data bug. It's also why I prefer the lookup layer to return a machine-readable error object rather than a text blob someone has to parse later.
For resilience, cache validated results and keep the cache conservative. A 24-hour TTL is a sensible default in most systems because EORI status changes slowly, and the point of the cache is to cut repeated lookups, not freeze reality. If the live service is unstable, queue retries with backoff and return a soft failure path that lets operations review the record without blocking the entire checkout.
A clean contract makes the whole thing easier to consume.
eori_format_invalidmeans the input never reached the registry.eori_invalidmeans the authority said no.service_unavailablemeans the authority couldn't answer.eori_lookup_pendingmeans your system should retry or route to manual review.
That separation matters because a frontend or billing engine should react differently to each one. A typo needs inline correction. A transient authority failure needs graceful degradation. A hard invalid needs a real rejection.
Edge Cases, Failures, and Consent-Gated Responses
The most common production surprise is a number that looks fine but doesn't come back as active. When that happens, don't accept it without challenge, because a syntactically valid EORI is still just a string until the authority confirms it. Return an explicit invalid status and make the caller decide whether to stop the order, ask for a correction, or send the case to manual review.
Consent-gated responses are another practical wrinkle. UK guidance says the GB checker can confirm validity and, when the holder has consented, show the name and address, while the same checker also covers XI and EU-issued EORI numbers in the UK context. That means your UI has to handle three states cleanly, valid with details, valid without public details, and invalid. HMRC EORI checking guidance
Don't treat missing name and address as a failure. In many cases, it just means the holder didn't consent to sharing public details.
Service instability is the failure mode nobody wants but everyone sees eventually. If the authority returns a transient error during a busy window, retry with backoff, then fall back to a soft-pass flow that flags the record for review rather than blocking the entire transaction. That's especially important in checkout, where a customs service outage should not behave like a hard business outage.
The final edge case is the stale-but-still-visible record. The European Commission guidance notes that deletion can happen only 10 years after expiry, so old records can linger in the ecosystem longer than people expect. If you see a formerly valid number now failing, treat it as a status change, not a formatting mystery, and route it to a human who can ask the customer or supplier for updated registration data. European Commission EORI guidance
The recovery pattern is always the same. Preserve the raw input, store the normalized version, keep the authority response, and expose the exact failure type to your caller. That gives support, finance, and engineering the context they need when a shipment stalls or an onboarding batch starts to fail.
Wiring EORI Checks Into Checkout, Billing, and KYC
In checkout, validate after the customer enters the number and before you apply any tax exemption or customs-dependent logic. That timing gives the user an immediate inline error if the number is malformed or rejected, and it keeps the order state from drifting ahead of the compliance check. In billing, persist the verified EORI alongside the company name and address so invoice generation doesn't have to re-check the registry every time.
For supplier onboarding and KYC, batch the validations. These flows are usually overnight or queue-driven, so they benefit from caching and retry handling more than they benefit from instant UI feedback. If you're screening vendors, a validated EORI becomes one of several identity signals, not the only one, and it should live in the same record as your other compliance artifacts.
A production checklist keeps the implementation from getting brittle.
- Monitor authority health: watch the EC and HMRC status pages before you assume your code is broken.
- Alert on spikes: sudden validation failures often point to routing issues or service instability.
- Keep error codes stable: client apps should not need to change every time you refine an upstream message.
- Rotate cache keys when data changes: if a customer says their registration changed, don't keep serving the old answer.
- Store the raw response: support will need it when a shipment or invoice needs explaining.
If you want to avoid building that wrapper yourself, TaxID exposes structured validation responses for tax and company identifiers and fits naturally into B2B checkout, billing, and KYC flows. It's the kind of layer that sits between your product and the registry so your team can keep the user experience stable when the upstream service isn't.
If you're wiring EORI validation into checkout or supplier onboarding, TaxID can give you a clean API surface instead of a fragile registry integration. Visit TaxID to see how teams plug validation into billing, compliance, and customs-adjacent workflows without rebuilding the same lookup logic twice.