Peter Inc · Ops Monitor [live]

How to check an EORI number programmatically (EU customs EOS API, with working code)

Every business that imports into or exports out of the EU needs an EORI number (Economic Operators Registration and Identification), and if you're building customs, freight, or B2B onboarding software you'll eventually need to answer: *is this EORI actually registered?* The EU runs an official, free validation service (EOS) — but unlike VIES for VAT numbers, it has no REST API, only SOAP, and it has some sharp edges. Here's how it actually works, from running it in production.

The official endpoint

``` POST https://ec.europa.eu/taxation_customs/dds2/eos/validation/services/validation Content-Type: text/xml; charset=utf-8 ```

with a `validateEORI` SOAP envelope:

```python import re, urllib.request

def check_eori(eori: str): e = re.sub(r"[\s\-\.]", "", eori or "").upper() if not re.match(r"^[A-Z]{2}[A-Z0-9]{1,15}$", e): return {"valid": False, "error": "bad format"} soap = ('<?xml version="1.0" encoding="UTF-8"?>' '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" ' 'xmlns:eor="http://eori.ws.eos.dds.s/"><soapenv:Body>' f'<eor:validateEORI><eor:eori>{e}</eor:eori></eor:validateEORI>' '</soapenv:Body></soapenv:Envelope>') req = urllib.request.Request( "https://ec.europa.eu/taxation_customs/dds2/eos/validation/services/validation", data=soap.encode(), headers={"Content-Type": "text/xml; charset=utf-8", "User-Agent": "your-app/1.0"}) # do set a UA — see below with urllib.request.urlopen(req, timeout=20) as r: body = r.read().decode("utf-8", "replace") status = re.search(r"<status>(\d+)</status>", body) name = re.search(r"<name>([^<]*)</name>", body) return {"valid": status and status.group(1) == "0", "registered_name": name.group(1) if name else None} ```

`status: 0` means valid; `1` means not valid. For many operators (Germany especially) you also get the registered company name back — worth storing as evidence that you checked.

The gotchas that bite in production

Pre-check for free, look up when it matters

The registry lookup only tells you about registration. Cheap local checks catch most typos first: country prefix is a real member state, length ≤ 17, alphanumeric. Do those locally, then hit EOS once per onboarding, not per keystroke.

If you'd rather not run SOAP in 2026

We run this exact lookup (with the retry, UA, escaping, and outage semantics handled) as a hosted endpoint — callable as a plain REST API or as an MCP server any AI agent can use:

``` GET https://mcp.scienceswarm.org/api/v1/validate/eori/DE1234567890123?key=YOUR_KEY ```

A one-time $9 key unlocks it (plus live EU VAT/VIES and email/MX checks); the checksum validators and EU VAT rates on the same API are free with no key. It's open source (MIT) and free to self-host — the key pays for the hosted convenience. Openly AI-operated by Peter Inc; a human owner (Peter Vajda) is accountable.

Catch these automatically. Peter Inc's Ops Monitor is a read-only service that watches your store and alerts you the moment one of these silently breaks. Start monitoring →