DEV Community

Fast Data
Fast Data

Posted on

How to check a new supplier's company data (LEI, register number, VAT) with Python before the first payment

Before a new supplier gets paid for the first time, someone in finance usually does the same three checks by hand: Does this company officially exist? Is the VAT number valid? Who is behind it? Each answer lives in a different place, and none of them is fun to copy-paste.

Two of those places are official and free to query:

  • GLEIF, the global registry of Legal Entity Identifiers (LEI): legal name, legal form, register number, addresses, status and parent companies.
  • VIES, the European Commission's service that confirms whether an EU VAT number is valid.

This post shows a short script that runs both checks for a whole supplier list and returns one row per company with warning flags.

Disclosure: I built the EU Company Check (KYB) actor used here. It only queries the official GLEIF and VIES APIs: no register scraping, no data about people.

Why bother: one in five German LEIs has lapsed

I asked the official GLEIF API today (10 November 2026):

Scope LEIs
Worldwide 3,489,248
Germany (legal address) 259,636
Germany, status ISSUED (renewed) 186,894
Germany, status LAPSED (not renewed) 55,763

A lapsed LEI is not proof of anything bad, but it means the company stopped confirming its data every year. Combined with a VAT number that VIES does not recognise, it is a good reason to pick up the phone before you pay.

The script

# pip install apify-client
import csv, os
from apify_client import ApifyClient

client = ApifyClient(os.environ["APIFY_TOKEN"])

suppliers = [
    "SAP SE; DE143454214",          # name + VAT number after a semicolon
    "Siemens Healthineers AG",      # name only
    "HRB 719915",                   # German register number
    "Nonexistent Testfirma XYZ GmbH",
]

run = client.actor("fast_data_asap/dach-company-kyb-official").call(run_input={
    "companies": suppliers,
    "country": "DE",          # filter matches to German entities
    "includeParents": True,   # add direct and ultimate parent
})
rows = list(client.dataset(run["defaultDatasetId"]).iterate_items())

with open("supplier_check.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["query", "legalName", "registeredAs", "lei", "registrationStatus", "ultimateParent", "vatValid", "flags"])
    for r in rows:
        parent = (r.get("ultimateParent") or {}).get("legalName", "")
        vat = (r.get("vat") or {}).get("valid", "")
        w.writerow([r["query"], r.get("legalName"), r.get("registeredAs"), r.get("lei"),
                    r.get("registrationStatus"), parent, vat, "; ".join(r.get("flags", []))])

needs_review = [r for r in rows if r.get("flags")]
print(f"{len(rows)} checked, {len(needs_review)} need a human look")
Enter fullscreen mode Exit fullscreen mode

The input accepts a name, an LEI or a register number and detects which one it is. A VAT number can follow a semicolon.

What came back

This is the real result of a test run on 10 November 2026:

Query Legal name Register no. LEI status Ultimate parent VAT Flags
SAP SE; DE143454214 SAP SE HRB 719915 ISSUED – valid Multiple matches (2) - showing 1
Siemens Healthineers AG Siemens Healthineers AG HRB 237558 ISSUED Siemens Aktiengesellschaft – –
HRB 719915 SAP SE HRB 719915 ISSUED – – –
Nonexistent Testfirma XYZ GmbH – – – – – No match

Three things worth noticing:

  1. The parent company comes for free. Siemens Healthineers shows Siemens AG as direct and ultimate parent, with its LEI. That is the question procurement teams ask most often and the hardest one to answer by hand.
  2. "No match" is a result, not an error. The made-up company returns a row flagged No match (and is not charged), so your list stays complete and the gap is visible.
  3. Even big names get flagged. GLEIF has two records with the exact legal name "SAP SE". The actor picks the active, issued one and tells you it had a choice. Register numbers are not unique across German register courts either, which is why the country filter matters.

The flags, and what to do with them

Flag What it means Typical next step
No match Nothing in GLEIF for this name/number Ask the supplier for its LEI or register extract
LEI lapsed (not renewed) Company stopped confirming its data Check if the company is still active
Fuzzy name match - verify No exact name, closest full-text hit shown Compare address and register number
VAT invalid VIES does not confirm the VAT number Do not pay before clarifying
Name differs from VAT name VAT number belongs to a differently named entity Check for typos or a different group company
Multiple matches (n) Several records fit Add the register number or LEI

The point is not to block payments automatically. It is to let the 90% of clean rows through and put the rest in front of a person.

Running it on every new supplier

Two easy ways to make this routine:

  • In your ERP export: run the script nightly on suppliers created that day and post rows with flags to Slack or e-mail.
  • For AI agents: the actor is available through Apify's MCP server, so an accounts-payable agent can call it before it proposes a payment. Our MCP setup lists it in the finance pack.

For a fuller check, combine it with an IBAN validation and a screen against BaFin warnings.

Limits

  • Only companies with an LEI are in GLEIF. Many small businesses never needed one, so No match for a local craftsman is normal.
  • This is not a commercial register extract, a credit check or legal advice. It confirms identity data from official sources; the decision stays with you.
  • VIES depends on national systems and is sometimes unavailable; the actor flags VAT check unavailable instead of guessing.

Cost

$0.004 per company found; rows without a match are free. Checking 100 new suppliers a month costs at most $0.40.

Try it

EU Company Check (KYB) on Apify

Question for finance and procurement folks: which check do you do first when a new supplier shows up, and which one has actually caught a problem for you?

Top comments (0)