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")
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:
- 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.
-
"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. -
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
countryfilter 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 matchfor 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 unavailableinstead 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)