Your first European B2B customer reaches checkout, enters a VAT number, and waits. Your code calls VIES, the request times out, and your fallback logic marks the number invalid. The customer is charged VAT they shouldn't owe, support gets a ticket, and finance gets an invoice nobody can defend. That failure isn't a tax problem. It's an engineering problem, and company number lookup sits right in the middle of it.
Table of Contents
- Why Company Number Lookup Is Harder Than It Looks
- The Four-Stage Lookup Workflow
- Making Your First API Request with Node.js and Python
- Handling Errors and Service Outages Gracefully
- Caching Strategies for Fast Checkout Flows
- Integrating Lookup into Checkout and Billing Flows
- Production Checklist for Your Lookup Integration
Why Company Number Lookup Is Harder Than It Looks
A German customer pastes a VAT number into your checkout on a Monday morning. The format looks right, your regex passes, and then the remote check returns nothing useful because a national database is unavailable. If your code treats that silence as invalid, you've just blocked a legitimate buyer or applied the wrong tax treatment.

The root cause is architectural. There's no single global company database to query. The World Bank Entrepreneurship Database covering 188 economies from 2006 through 2024 collects data from separate business registries and statistics agencies, and its total firm measure counts privately held limited liability companies. That structure tells you something important about production systems. A company number only means something inside the jurisdiction and legal form that issued it.
A Number Can Exist While the Company Is Disappearing
The UK shows the scale problem clearly. The register held 4,716,126 companies at the end of March 2021, and 305,890 were in dissolution or liquidation, leaving an effective register of 4,410,236, according to Companies House register statistics for 2020 to 2021. A lookup that returns only yes or no hides that distinction.
- Format valid: The characters match local rules, which proves almost nothing.
- Registry found: An entity exists or existed under that identifier.
- Currently effective: The status supports the transaction you're about to process.
Those are three different answers, and billing code needs the third one. By March 2024 the UK register had grown past 5.3 million companies, so stale snapshots and local copies drift fast. You need a live, jurisdiction aware lookup that returns status, legal name, and address, not a pattern match dressed up as verification.
A regex tells you a number could be real. It never tells you the company behind it is real right now.
The Four-Stage Lookup Workflow
Reliable implementations separate syntactic validation, jurisdiction routing, authoritative lookup, and evidence capture. Mixing those stages into one API call makes failures impossible to diagnose. Keeping them separate gives you a specific place to fix each class of problem.

Normalize and Validate Before You Spend a Remote Call
Start by uppercasing the country prefix and removing spaces or punctuation only where that jurisdiction permits it. Always preserve the original input for audit purposes, because support teams need to see what the customer typed. Then apply country specific length and character rules. The European Commission VIES service definition accepts a country code matching [A-Z]{2} and a VAT number of 2 to 12 permitted alphanumeric or symbol characters, which is a useful reminder that generic rules don't travel well across borders.
Routing comes next, and it's where many teams get VIES wrong. VIES isn't a central EU database. It's a search engine that retrieves data from national VAT databases at query time. Your German lookup depends on German systems being up, even if the EU front door responds fine.
Capture Evidence as a First Class Output
Store more than a boolean. A useful record includes the following fields.
- Queried identifier and jurisdiction: The normalized value plus the original input.
- Returned legal name and address: What the authority sent back, exactly as received.
- Timestamp and request reference: When you asked and any consultation reference supplied.
- Exact status category: Valid, invalid, or indeterminate, never a collapsed true or false.
If you're designing the data layer behind this, the guide to building a company data API covers how to structure those records for reuse across billing and compliance. Evidence capture feels like overhead until an auditor asks why you applied reverse charge eighteen months ago. Then it's the only part of the system that matters.
Making Your First API Request with Node.js and Python
The first integration decision is boring and consequential. Call the SOAP based VIES service directly, or call a REST wrapper that returns clean JSON. In production billing systems, the wrapper wins on operability almost every time, because you get standardized errors instead of brittle XML fault strings.

In Node.js, the shape is a single fetch call. You send the country code and number, await the JSON body, and branch on a machine readable status field rather than the HTTP code alone. In Python, the same flow uses requests with an explicit timeout, because a lookup without a timeout will eventually hang a checkout worker. Set that timeout deliberately and handle the exception as an indeterminate result, not a failure of the customer.
Reading the Response Like a Billing System
Parse four things from every successful response. The valid flag drives tax logic. The registered company name goes on the invoice and into your customer record, so you can spot mismatches against what the user typed. The registered address supports place of supply reasoning. The request reference belongs in your audit log next to the timestamp.
Map those fields into your own model immediately. Don't store raw provider payloads as your only record, because provider shapes change and your finance team needs stable columns. Do keep the raw payload in a separate evidence table for disputes. Services like TaxID follow this pattern by returning validation status, company name, and address in JSON across 31 countries through one REST endpoint, which saves you from maintaining per country parsers.
The short walkthrough below shows the request and response cycle end to end.
If you're wiring this into a storefront rather than a SaaS signup, the patterns in this overview of Shopify API integration from Presidio are worth reading alongside your billing work. The integration concerns rhyme. Webhooks, retries, and idempotent writes matter in both places, and a VAT check that fires twice shouldn't create two conflicting customer records.
Handling Errors and Service Outages Gracefully
Most lookup bugs I've debugged weren't parsing bugs. They were classification bugs, where four different situations collapsed into one error message that told the customer their number was wrong.

Four Outcomes That Must Never Share a Code Path
Malformed input fails your local format rules. Return immediate, specific feedback in the form and don't call the remote service. Invalid means the authority answered and said no. That's a real rejection, and it's safe to ask the customer to recheck the number.
Not yet activated is the awkward middle case. The Your Europe guidance on checking VAT numbers through VIES notes a number can appear invalid because it hasn't been activated for intra-EU transactions or hasn't propagated after registration. Telling that customer they're fraudulent is unfair and usually wrong. Temporary failure covers outages, timeouts, and concurrency limits. The lookup didn't complete, so you know nothing about the number.
- Retry transient classes only: Use exponential backoff with jitter and cap your attempts.
- Never retry a definitive invalid: You'll burn quota and annoy rate limiters for no gain.
- Queue instead of blocking: Let checkout finish with verification pending where risk allows.
Practical rule: An unavailable registry is a statement about the registry, not about your customer.
Standardizing these outcomes into stable codes pays off across your whole API surface, and the guidance on REST API response codes best practices is a solid reference for naming them consistently. Customer messaging matters just as much. Say you're unable to verify right now and will confirm by email. Don't say the number is invalid when your system couldn't reach a server in another country.
Caching Strategies for Fast Checkout Flows
Every remote lookup inside checkout adds latency you don't control. National databases respond at their own pace, and your conversion rate absorbs the delay. Caching isn't an optimization here. It's what makes validation inside a payment flow viable at all.
Cache Positives Aggressively and Negatives Barely
Cache authoritative positive results with an explicit freshness policy. A 24 hour TTL on valid numbers is a sensible default for checkout because company registrations rarely change hour to hour, and you'll revalidate at invoice time anyway. Key the cache by country code plus normalized number, typically in Redis, so repeat buyers and returning sessions skip the network entirely.
Invalid results deserve opposite treatment. A number that failed yesterday might be activated today, especially for newly registered companies. Cache negatives briefly if at all, and never cache indeterminate results as if they were answers. An outage stored in Redis becomes a self inflicted outage that outlives the outage itself.
- Stale while revalidate: Serve the cached positive instantly and refresh it in the background.
- Fresh check at invoice issuance: Revalidate before applying a tax exemption when the cached entry is stale.
- Cache the evidence too: Keep name, address, and reference with the cached status.
The deeper mechanics of TTL selection and thundering herd protection are covered in the notes on VAT API rate limiting and caching, which is worth reviewing before you set production limits. One trap deserves emphasis. Don't let the cache become your audit record. Caches evict. Your evidence table shouldn't.
Speed also changes user behavior in a helpful way. When validation returns instantly, you can run it on blur as the customer leaves the VAT field, show the registered company name back to them, and let them catch typos themselves. That confirmation step prevents a surprising share of support tickets, because people recognize their own legal name faster than they recognize a mistyped digit.
Integrating Lookup into Checkout and Billing Flows
Wiring the lookup into a real flow means deciding where it runs, what it blocks, and what it merely informs. Those decisions differ between a React checkout, a Stripe subscription flow, and a back office invoicing job, but the skeleton stays the same.
A Workable Sequence for Checkout and Invoicing
Validate on blur in the checkout form, not on submit. The customer gets feedback while they're still looking at the field, and your submit handler stays fast because the result is already cached. Show the returned legal name as confirmation text. If the name doesn't match what they expect, they'll fix the number before paying.
At payment time, pass the verified status into your billing logic. For a Stripe based SaaS selling cross border within the EU, a valid VAT number from another member state is what justifies reverse charge treatment on the invoice. Store the validation evidence ID against the Stripe customer and the invoice, so finance can trace any invoice back to the exact lookup that supported it. Revalidate before generating the final invoice if the cached result has aged out, because the invoice is the document an auditor will examine.
Platform constraints shape where this code can live. If you're extending a hosted checkout, the current state of custom checkout solutions for Shopify from Shugert explains which extensibility points let you run validation before purchase completes. In custom builds you have more freedom, but the same principle holds. Validate as early as the platform allows, and never let a slow registry hold the payment step hostage.
Block or Allow When Verification Is Pending
This is a risk decision, not a technical one. For a low value self serve subscription, allow the purchase, charge VAT provisionally or flag the account for review, and complete verification asynchronously. Blocking a legitimate customer because a national database is down costs more than a later invoice correction. For high value orders, annual contracts, or anything involving payouts, hold fulfillment until verification completes.
- Low risk self serve: Allow checkout, verify in the background, correct the invoice if needed.
- Mid value B2B orders: Allow payment, hold provisioning until the lookup resolves.
- High value or payout flows: Require completed verification plus additional checks before money moves.
What a Valid Number Does Not Prove
A valid company number proves an entity exists in a registry. It doesn't prove the person at the keyboard works for that entity, controls it, or is authorized to buy on its behalf. Anyone can copy a real VAT number off a supplier's invoice and paste it into your checkout to get reverse charge pricing. Your lookup will happily confirm it.
Regulators are closing exactly that gap. In the UK, Companies House identity verification guidance describes verification becoming mandatory from 18 November 2025, with directors and people with significant control receiving personal verification codes. The reform aims to deter people from using companies for illegal purposes, which is an official admission that identifier lookup alone isn't enough for fraud prevention.
Legal existence and authorization to transact are different claims. Your system should check them separately.
Build a layered model that matches effort to risk. Invoice validation for a small subscription needs registry and VAT status. Supplier onboarding needs beneficial owner checks, address verification, and sanctions screening. Payouts need controller verification and evidence of authority. Dormant entities, impersonated names, and outdated addresses all pass a naive lookup and fail these deeper checks.
Ongoing monitoring closes the loop. A customer verified at signup can be dissolved, renamed, or struck off later, and your recurring billing will keep applying yesterday's tax treatment unless you recheck periodically. Schedule revalidation for active B2B accounts and alert finance when a previously valid number turns invalid. That single job catches a category of quiet compliance drift that one time onboarding checks never will.
Production Checklist for Your Lookup Integration
Shipping the happy path takes an afternoon. Shipping the version that survives a registry outage during your biggest sales week takes a checklist.
Before You Go Live
- Monitor outcome rates separately: Track valid, invalid, and indeterminate percentages so an outage shows up as a spike in indeterminate, not a mystery drop in conversions.
- Log the full audit trail: Every lookup stores input, normalized value, jurisdiction, response, timestamp, and reference.
- Alert on outage patterns: Notify the on call engineer when one country's failures climb while others stay healthy.
- Write customer copy in advance: Prepare pending verification messages for checkout, email, and support macros.
- Test the failure paths: Simulate timeouts and unavailable responses in staging, not just valid and invalid fixtures.
- Set retry and cache policies explicitly: Document backoff caps, TTLs, and the revalidation trigger at invoice time.
Keep the response guide where engineers and support staff can both find it.
| Outcome | Meaning | Recommended Action |
|---|---|---|
| Valid | Authority confirmed the number and returned entity details | Apply correct tax treatment and store evidence |
| Invalid | Authority answered and the number isn't registered or active | Ask customer to recheck and don't apply exemption |
| Malformed | Input failed local format rules for the jurisdiction | Show inline format guidance and skip the remote call |
| Unavailable | Registry outage, timeout, or concurrency limit | Queue for retry with backoff and mark verification pending |
Run one final review with finance before launch. Engineers see status codes, but finance owns the consequences of a wrong reverse charge decision. Agree in writing which outcomes permit exemption, which require a fresh lookup, and how long evidence is retained. That conversation is cheap now and expensive during an audit.
If you'd rather not build and maintain the VIES wrapper, caching layer, and outage handling yourself, TaxID gives you company and VAT validation across 31 countries through one REST endpoint with standardized JSON responses and machine readable error codes. Start on the free tier, wire it into your checkout or billing flow this week, and let your team spend its time on your product instead of national registry quirks.