EU Company ID Resolver – VAT, KvK, KBO & LEI avatar

EU Company ID Resolver – VAT, KvK, KBO & LEI

Pricing

from $34.00 / 1,000 entity resolveds

Go to Apify Store
EU Company ID Resolver – VAT, KvK, KBO & LEI

EU Company ID Resolver – VAT, KvK, KBO & LEI

EU company ID resolver: turn a VAT number, KvK, KBO, SIREN, SIRET, LEI or company name into all the other identifiers plus official name, legal form and status, each with source and confidence, from VIES, GLEIF, KBO, SIRENE and the Peppol Directory. Not found comes with a reason, never a guess.

Pricing

from $34.00 / 1,000 entity resolveds

Rating

0.0

(0)

Developer

Leyten Data

Leyten Data

Maintained by Community

Actor stats

0

Bookmarked

3

Total users

2

Monthly active users

a month ago

Last modified

Share

What does EU Company ID Resolver do?

EU company ID resolver: give it one identifier of a company, get all the others back. A VAT number, a Dutch KvK number, a Belgian KBO number, a French SIREN or SIRET, an LEI, or a company name with a country in; the VAT number, the national register number, the LEI, the official name, legal form and status out, each field with the register it came from and a confidence score. Under the hood it combines VIES, GLEIF, the Belgian KBO public search, the French SIRENE register and the Peppol Directory, and it never guesses: what cannot be derived comes back as not found with the reason.

Why every EU customer file needs this

Your ERP has VAT numbers, your credit insurer wants KvK or KBO numbers, your bank asks for an LEI, your e-invoicing provider needs a Peppol participant ID, and the sanctions screening tool wants the registered name exactly as the register spells it. Every system keys the same company differently, and nobody publishes the crosswalk. Today that gap is closed by hand, or by a KYB subscription. This Actor is the crosswalk, per company, for a few cents, with every link explained.

FromToHowConfidence
Belgian VAT numberKBO number (and back)The KBO number is the VAT number without the prefix0.60 until confirmed (VIES for either number; the KBO page for the KBO number, and for the VAT number when its qualities list the enterprise as liable to VAT and VIES has not reported that number as not valid), then 1.00 verified
French VAT number, SIRETSIREN (and back)The SIREN is inside the VAT number and the SIRET; the key is computed0.60 until confirmed (VIES for either number, SIRENE for the SIREN), then 1.00 verified; 0.98 for the VAT number SIRENE lists when it contains the SIREN
SIRENSIRET, name, legal form, statusSIRENE (recherche-entreprises.api.gouv.fr)1.00
KBOname, legal form, status, seatKBO public search page1.00
VAT numbername, address, validityVIES; a member state that discloses neither name nor address is reported as such in outcome1.00
KvK / KBO / SIRENLEI, legal form, statusGLEIF registeredAs (the register number recorded with the LEI)the LEI 1.00 verified (legal form 0.97 verified, status 0.95) when that number is one the company has for sure: the input, a number its register confirmed, or the number inside an input VAT number or SIRET; otherwise at most 0.97 and no more than the number it was found by (0.90 for a KvK number from a Peppol card)
LEIKvK / KBO / SIRENthe same GLEIF field, read the other way0.97 (a SIRET 0.95); a KBO number rises to 1.00 verified when the KBO page confirms it, a SIREN when SIRENE does; a KvK number stays at 0.97
registered name (from VIES, the KBO, SIRENE or a Peppol card)LEI, legal form, statusGLEIF records of that name in the same country, at least 90% similar (or Minimum name similarity when higher); two records that match about equally well link neither0.95 × similarity, with the match in via; the LEI 1.00 verified only when its record lists a number the company has for sure, as in the first GLEIF row
KvK / KBO / SIREN / VATthe other numbersthe entity's Peppol business card, published by the company for e-invoicing0.90; raised to 1.00 verified when the register for the number confirms it (VIES for a VAT number, the KBO page for a KBO number, SIRENE for a SIREN), and a Belgian VAT number also when the KBO page lists the enterprise as liable to VAT and VIES has not reported that number as not valid; a KvK number stays at 0.90
name + countryeverything aboveGLEIF (LEI holders), Peppol Directory, and for France SIRENE; the best clear match above Minimum name similarity is taken, near-ties are reported as ambiguous≤ 0.95 × similarity for what the match gives, raised as above when a register confirms it (the match stays in via); the overall confidence stays ≤ 0.95 × similarity whatever the registers confirm, and ≤ 0.75 for one-word names or when GLEIF, the Peppol Directory or SIRENE did not answer

Facts are resolved in rounds until nothing new appears, so a KvK number can lead to a Peppol card, the card to a VAT number, VIES to the registered name, and the name to an LEI. A number that follows by arithmetic is never swapped for a different one from a Peppol card or a GLEIF record, whichever source answers first (also for a name, where the VAT number comes from the match): the contradiction is reported in warnings, a VAT number or SIRET that does not fit the other number of the pair is not taken either, and a GLEIF record that lists another register number than the company's is not linked at all. The KBO page or SIRENE record of the contradicting number is never looked up.

Honest about coverage

  • Netherlands. The KvK does not offer a free lookup API, and Dutch VAT numbers are assigned independently of the KvK number. A KvK number is therefore found only when the company has an LEI (GLEIF records it) or a Peppol business card that lists it; a VAT number for a KvK number only through Peppol. When neither exists the record says so, with that reason, instead of a number that might belong to someone else.
  • Belgium. Complete: the KBO number, the VAT number and the KBO public page are one chain; the LEI where one exists.
  • France. Complete: SIREN, SIRET, VAT and the SIRENE record are one chain; the LEI where one exists.
  • Other member states. VAT validity and, where the member state discloses it, the registered name from VIES; the LEI, legal form and status from GLEIF when you give the LEI or GLEIF has an LEI record under the company's name (for a VAT number, at least 90% similar to the name from VIES or a Peppol card); Peppol identifiers where the company is registered. No national register is consulted and no national register number is returned, also when GLEIF holds one; the record's notFound says which link is missing and why.
  • LEIs exist only for a minority of companies. lei: not found is the normal case for a bakery.

Features

  • Company identifier resolution across VIES, GLEIF, KBO, SIRENE and the Peppol Directory in one call
  • VAT number to KvK, KBO, SIREN, SIRET and LEI, and every direction back
  • Company name to VAT number, register number and LEI, with similarity score, alternatives and an explicit ambiguity flag
  • Official name, legal form (with ELF code) and status from the register that is authoritative for it
  • Source, confidence and verification per field, so every value in your master data has a provenance
  • Explicit notFound with a reason for every identifier that applies but could not be established; never a guess
  • Sole traders protected: when a register classifies the entity as a natural person, or when the only name has no legal form and no register confirms a legal person, the name and the registered address are dropped; directors, representatives and contact persons are never read from any source
  • Charged per resolved entity only; unrecognised inputs, dead ends, ambiguous names and numbers only derived by arithmetic are free
  • Runs through the Apify API, on a schedule, or from Make, Zapier or n8n through that API

Use cases

Master data teams: one key for every system

Run the VAT numbers from your ERP through the resolver and store kvk, kbo, siren, lei and the registered legalName next to them, with source and confidence as audit fields. Re-run quarterly; status: inactive and vatValid: false are the rows to look at.

// npm install apify-client
import { ApifyClient } from 'apify-client';
const client = new ApifyClient({ token: process.env.APIFY_TOKEN });
const run = await client.actor('lwsdjfls/eu-company-id-resolver').call({ identifiers: customers.map((c) => c.vatNumber) });
const { items } = await client.dataset(run.defaultDatasetId).listItems({ clean: true });
for (const { payload } of items) {
if (!payload.resolved) continue;
await updateCustomer(payload.input, {
legalName: payload.entity.legalName?.value ?? null,
kvk: payload.identifiers.kvk?.value ?? null,
lei: payload.identifiers.lei?.value ?? null,
status: payload.entity.status?.value ?? 'unknown',
confidence: payload.confidence,
missing: payload.notFound.map((n) => `${n.identifier}: ${n.reason}`),
});
}

Onboarding and KYB pre-checks

A new customer gives you a KvK or KBO number: get the registered name, legal form, status and VAT number in one call before the account is created, and hand the lei to the bank when it asks.

E-invoicing teams

Find the Peppol participant IDs a company is registered under from any of its numbers (peppolParticipants), and the reverse: a Peppol ID's company numbers.

How to use it

  1. Put whatever identifiers you already hold into Identifiers: a VAT number, a KvK or KBO number, a SIREN or SIRET, an LEI, or simply a company name. Mixing kinds in one run is fine, and a prefix such as KvK 24330087 removes any ambiguity. Set Default country when you pass bare names.
  2. Press Start and read one record. Every distinct input produces exactly one record, unless a maximum cost per run stops delivery (see Pricing), including the ones that resolved nothing and the ones the run had no time left for (see the limitations below; an input you repeat is looked up once and charged at most once, and blank lines are skipped), and each field carries the source that confirmed it together with a confidence. Nothing in the output is inferred silently.
  3. Run it over a customer or supplier file to fill the gaps in your CRM or ERP. The resolution view is the filled-in row per company; the notFound view lists what could not be resolved and why, which is the part you can actually act on.

A register that does not answer is reported as unavailable and never as "not found", so a bad afternoon at VIES cannot quietly empty a column in your master data. Where a number can be derived arithmetically, such as a Belgian VAT number from a KBO number, it is shown with confidence 0.60 and verified: false until a register confirms it, and it is never charged on its own.

Two boundaries are worth knowing before you plan around this. Dutch KvK numbers can only be reached through GLEIF or the Peppol Directory, because the KvK API is paid, so Dutch coverage is real but not complete. And the records are company data: directors, representatives and contact persons are never read from any source, and when a register classifies the entity as a natural person (the KBO entity type, SIRENE's entrepreneur individuel, GLEIF's sole-proprietor category) the name and the registered address are dropped, and the same happens when the only name comes from VIES or a Peppol card without a legal form and no register confirms a legal person. A sole trader gives you the identifiers, the legal form and the status. That is not a guarantee that nothing personal ever reaches a record: a search by name lists what it matched in alternatives, sole traders included.

curl "https://api.apify.com/v2/acts/lwsdjfls~eu-company-id-resolver/run-sync-get-dataset-items?token=YOUR_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"identifiers":["NL810433941B01","BE0403170701","Danone; FR"],"defaultCountry":"NL"}'

Input

FieldWhat it doesExample
identifiersOne per line: a VAT number, a prefixed register number (KvK 24330087, KBO 0403.170.701, SIREN 552032534, SIRET 55203253400703, LEI 72450020ONP2TGFMWS03) or a company name with country (Coolblue; NL). Bare numbers use the default country.["NL810433941B01", "KvK 24330087", "Danone; FR"]
defaultCountryCountry for bare numbers and names without a country suffix.NL
minNameSimilarityHow similar a registered name must be to a name you gave, or to link an LEI to an entity found by number (100 = identical).85
maxConcurrencyInputs resolved at the same time; every register keeps its own rate limit regardless.4
maxWaitMinutesHow long a member state's VIES service may keep failing before its VAT numbers are reported as unavailable (never as invalid). The wait also ends early enough for the run to deliver every record before its timeout.3
proxyConfigurationNot needed normally; only when VIES answers IP_BLOCKED.{ "useApifyProxy": false }
{
"identifiers": ["NL810433941B01", "KvK 24330087", "BE0403170701", "SIREN 552032534", "LEI 72450020ONP2TGFMWS03", "Danone; FR"],
"defaultCountry": "NL",
"minNameSimilarity": 85
}

Prefixes are understood in the usual spellings (KvK, KBO, BCE, ondernemingsnummer, SIREN, SIRET, LEI, VAT, BTW, TVA). A bare number is read with the default country; a bare name needs ; NL-style country or the default country.

Output

One record per distinct input:

identifiers.vat NL810433941B01 source input confidence 1.00 verified (VIES: valid)
identifiers.kvk 24330087 source gleif confidence 0.95 via GLEIF registeredAs at Handelsregister (KvK) (name match with GLEIF (100%))
identifiers.lei 72450020ONP2TGFMWS03 source gleif confidence 0.95 via name match with GLEIF (100%)
entity.legalName COOLBLUE B.V. source vies confidence 1.00 verified
entity.legalForm 54M6 · besloten vennootschap met beperkte aansprakelijkheid source gleif confidence 0.95
entity.status active source gleif
notFound (none)
  • identifiers: vat (with vatValid from VIES), kvk, kbo, siren, siret, lei, peppolParticipants
  • entity: legalName, legalForm (code and label), status (active, inactive, unknown) with statusDetail, registered address, and naturalPerson
  • every fact carries source (vies, gleif, kbo, sirene, peppol, derived or input), via (what it was derived or matched from), confidence (0–1) and verified (confirmed by the register that is authoritative for it)
  • notFound[]: each identifier that applies to the entity's country but could not be established, with the reason
  • alternatives[] (at most five, each at least 70% similar) and ambiguous for names that match more than one entity, sourcesConsulted, unavailable (registers that did not answer, or that were not looked up because the run's timeout was near), warnings, outcome in one sentence, and an overall confidence (the weakest link, including numbers only derived by arithmetic; for a name input at most 0.95 × the similarity of the match)

A Dutch VAT number: VIES confirms it and gives the registered name; GLEIF's LEI record for that name supplies the LEI, the KvK number and the legal form. They are linked by name, so they carry 0.95 × the similarity of the name (100% here); no free register can confirm a KvK number that would tie them to the company for sure.

{
"envelopeVersion": 1,
"id": "vat:NL:nl810433941b01",
"scope": "vat:NL:nl810433941b01",
"changeType": "added",
"sourceUrl": "https://search.gleif.org/#/record/72450020ONP2TGFMWS03",
"scrapedAt": "2026-09-15T23:11:34.340Z",
"firstSeenAt": "2026-09-15T23:11:34.340Z",
"hash": "18afba6481b1e07cf84d071f4c555c16f50bc69d306663808a2ed40cfcfd9806",
"previousHash": null,
"changes": [],
"summary": "Added: NL810433941B01: COOLBLUE B.V. (NL810433941B01, KvK 24330087, LEI 72450020ONP2TGFMWS03)",
"diffText": null,
"payload": {
"input": "NL810433941B01",
"inputType": "vat",
"country": "NL",
"resolved": true,
"outcome": "KVK via gleif (GLEIF registeredAs at Handelsregister (Kamer van Koophandel) (name match with GLEIF (100%))); LEI via gleif (name match with GLEIF (100%)); name from VIES",
"entity": {
"legalName": {
"value": "COOLBLUE B.V.",
"source": "vies",
"via": null,
"confidence": 1,
"verified": true
},
"naturalPerson": false,
"legalForm": {
"value": {
"code": "54M6",
"label": "besloten vennootschap met beperkte aansprakelijkheid"
},
"source": "gleif",
"via": "name match with GLEIF (100%)",
"confidence": 0.95,
"verified": false
},
"status": {
"value": "active",
"source": "gleif",
"via": "name match with GLEIF (100%)",
"confidence": 0.95,
"verified": false
},
"statusDetail": "GLEIF entity status ACTIVE, registration ISSUED",
"address": {
"value": {
"lines": [
"WEENA 00664",
"3012CN ROTTERDAM"
],
"city": null,
"postalCode": null,
"country": "NL"
},
"source": "vies",
"via": null,
"confidence": 1,
"verified": true
}
},
"identifiers": {
"vat": {
"value": "NL810433941B01",
"source": "input",
"via": null,
"confidence": 1,
"verified": true
},
"vatValid": {
"value": true,
"source": "vies",
"via": null,
"confidence": 1,
"verified": true
},
"kvk": {
"value": "24330087",
"source": "gleif",
"via": "GLEIF registeredAs at Handelsregister (Kamer van Koophandel) (name match with GLEIF (100%))",
"confidence": 0.95,
"verified": false
},
"kbo": null,
"siren": null,
"siret": null,
"lei": {
"value": "72450020ONP2TGFMWS03",
"source": "gleif",
"via": "name match with GLEIF (100%)",
"confidence": 0.95,
"verified": false
},
"peppolParticipants": [
"0106:24330087",
"9944:nl810433941b01"
]
},
"notFound": [],
"alternatives": [],
"ambiguous": false,
"sourcesConsulted": [
"GLEIF",
"Peppol Directory",
"VIES"
],
"unavailable": [],
"warnings": [],
"confidence": 0.95,
"checkedAt": "2026-09-15T23:11:34.340Z"
}
}

Pricing

EventNameWhat it meansPrice
apify-actor-startActor startCharged by Apify when a run starts: once for a run of up to 1 GB of memory, and once more for every extra GB. It covers the first five seconds of compute.$0.00005 per run up to 1 GB of memory ($0.05 per 1,000)
entity-resolved (primary)Entity resolvedOne input (VAT, KvK, KBO, SIREN/SIRET, LEI or name + country) resolved to at least one further identifier or entity fact (official name, legal form, status), each with its source and confidence, across VIES, GLEIF, the Belgian KBO, the French SIRENE register and the Peppol Directory. Not charged when nothing beyond the input could be established, or when the only extra number is one derived arithmetically from the input that no register confirmed.$0.04 per resolved entity ($40.00 per 1,000)

These are list prices, which the Free plan pays. Paid Apify plans pay less for every event except the Actor start: 5% on Apify's Bronze tier, 10% on Silver and 15% on Gold and higher.

One run takes at most 20,000 identifiers, and an identifier that appears twice is looked up and returned once and charged at most once. With the default limits the worst case is 20,000 resolved inputs × $0.04 = $800.00, plus one Actor start of $0.00005 (the default memory is 256 MB and the maximum 1 GB, so every run has exactly one start event): $800.00005 in total. A customer file of 1,000 companies costs at most 1,000 × $0.04 = $40.00, plus the $0.00005 start. Inputs that resolve to nothing new (an unknown KvK number, a name nobody has registered), ambiguous names, unrecognised inputs and inputs a run had no time left for (see the limitations) are free. A Belgian or French number whose derived counterpart (VAT number, KBO number, SIREN) no register confirmed counts as nothing new too: arithmetic on your input is not a lookup, so a run during an outage of every register costs only the $0.00005 start. Each input consults at most five sources: VIES, GLEIF, the KBO public search, SIRENE and the Peppol Directory.

Example: a recorded run of 11 mixed inputs on 15 September 2026 resolved 9 of them: 9 × $0.04 = $0.36, plus $0.00005 for the start, $0.36005 in total. The two free inputs were a KvK number that neither GLEIF nor the Peppol Directory knows and a KBO number that fails its check digits. The 9 charged inputs were 5 companies: you pay per input that resolves, so a company's VAT number, KvK number, LEI and name sent as four inputs cost 4 × $0.04. The two identifiers Apify Console fills in for a first try, NL810433941B01 and KvK 24330087, both resolved in that recording: 2 × $0.04 + $0.00005 = $0.08005.

To cap what a run can cost, set a maximum cost per run in the run options in Apify Console: when the next resolved input would go over that amount, the Actor stops writing records and logs that delivery stopped at the spending limit, so the records after that point, free ones included, are not in that run's dataset and those inputs need a new run.

FAQ

Why is kvk not found for a Dutch VAT number that clearly belongs to a real company?

Because no free public register maps the two. The KvK API is paid. The resolver links them only through GLEIF (companies with an LEI) or a Peppol business card. The reason is in notFound, and no number is invented.

The name matched something, but is it the right company?

alternatives lists up to five entities that came close (at least 70% similar, whatever minimum you set) with their similarity, and ambiguous says when two matched equally well. Registers can confirm the numbers of the company the name matched, and those fields then show 1.00 verified with the match in via; that proves the numbers belong to that company, not that it is the one you meant. The overall confidence therefore stays at most 0.95 × the similarity of the match (0.79 for an 83% match), and one-word names are capped at 0.75 with a warning. The same holds for an LEI found through the registered name of a company you gave by number: it keeps 0.95 × the similarity and the match in via, unless its GLEIF record lists a number the company has for sure. An ambiguous name is free: it carries only those candidates, not a resolved entity, so pick one and resolve it by number. When it matters, resolve by number instead of by name.

Does it give the same answer as a KYB provider?

For the identifiers, it reads the primary registers themselves (VIES, GLEIF, KBO, SIRENE). What it does not do is beneficial-owner checks, credit scores or document retrieval.

Why is there no name for a sole trader, and is EU company ID resolution GDPR-compliant?

Sole traders (eenmanszaken, entreprises individuelles, natural persons in the KBO) are natural persons, so their data falls under the GDPR. The resolver delivers their identifiers and legal form but withholds the name and address when a register classifies the entity as a natural person, and also when the only name available comes from VIES or a Peppol card without any legal form in it and no register confirms a legal person. Directors, representatives and contact persons are never read from any source: the KBO page's "Functies" block, GLEIF's relationship data and SIRENE's dirigeants are skipped by design. French companies that opted out of publication keep their name withheld. Results exist only in your own run's dataset; the Actor keeps no state between runs.

What are the limitations?

  • VIES member-state outages delay or block the VAT check; the VAT number is then reported as unavailable, never as invalid, and the other sources still answer.
  • VIES gives no name or address when a member state does not disclose them, and the record then says so in outcome. For a VAT number from a member state other than the Netherlands, Belgium or France, GLEIF is searched only by the company's name, so when VIES withholds the name such a number gets no LEI, legal form or status unless a Peppol business card carries the company name.
  • GLEIF covers LEI holders only; KBO and SIRENE cover Belgium and France; the Netherlands has no free register API.
  • The Peppol Directory is asked at most once every two seconds for the whole run, whatever Parallel resolutions is set to, and nearly every input (a name, or a VAT, KvK, KBO, SIREN or SIRET number) needs at least one Peppol lookup; a Belgian or French number needs two (the business card of its register number and the one of its VAT number). Plan on 2 to 4 seconds per input: up to about 67 minutes for 1,000 identifiers and about 22 hours for the 20,000 maximum. When the directory still refuses, it is listed in unavailable, and what only a Peppol card can give, such as the VAT number for a KvK number, is missing from that record.
  • The default run timeout is 24 hours, which fits the 20,000 maximum at 4 seconds per input and 0.1 seconds to deliver each record (about 23 hours); a caller can set another in the run options. A run whose input does not fit in its timeout stops its lookups early enough to deliver every record (it keeps up to 0.1 s per record for that) and ends as succeeded (partial), not timed out with nothing delivered; its status message counts the inputs it did not process. Those come back free, with resolved: false, an outcome that starts with not processed: and says to submit them again, and all sources: not looked up (the run's timeout was near) in unavailable. A timeout too short even to deliver a record for every input (0.1 s each: about 33 minutes for 20,000 inputs) leaves no time for lookups at all: nothing is looked up or charged, every input comes back as not processed, and if the timeout comes before all those records are written, the run ends timed out without the rest. Until the time for lookups is up, inputs start and requests are sent and retried as usual; a request sent shortly before that gets a shorter timeout, never below 10 seconds, and after that nothing more is sent or retried, VIES included. In a run shorter than five minutes up to the last 30 seconds of that time can go unused, so that such a request still ends before the run must finish. The sources an input in progress did not ask are listed in unavailable as not looked up (the run's timeout was near), one that did not answer within the shorter timeout, or whose next attempt could no longer be made in time, as no answer in the time the run had left before its timeout, and an identifier one of them could have given is listed in notFound with that reason (a GLEIF legal form whose name was not looked up shows its code as its label). A warning in the record names what was not asked. Such an input keeps what the other sources gave and is charged when it resolved something, like any record, so submitting it again would be charged again; its warning says to submit it again only when nothing was charged. Submit the inputs that were not processed again in a run with a longer timeout, or split the input over smaller runs.
  • Names are matched on the registered legal name; trade names are not searched except where Peppol cards carry them.
  • The Actor reads the public interfaces of VIES (European Commission), GLEIF, the FPS Economy's KBO public search, the French government's recherche-entreprises and the Peppol Directory, and is affiliated with none of them.

Support

A register with a free public lookup that should be added (Denmark's CVR, Norway's Brønnøysund, Finland's YTJ)? Open an issue on the Actor page.

Last live run against VIES, GLEIF and the Peppol Directory: 21 September 2026, with the input the Console form opens with (NL810433941B01 and KvK 24330087).