GET /company/resolve
Resolves a French company identifier into its official identity card: legal name, active/ceased status, legal form, NAF activity code with label, workforce band, creation date and head-office address. The answer is served from a local store built from the official INSEE Sirene register (30M+ legal units, monthly stock), so a lookup resolves in milliseconds with no network call and no secret at request time.
Use it when an agent needs a grounded answer before acting on a French
counterparty - “does this SIREN exist, and is the company still active?”,
“what is the legal name and head office behind this SIRET on an invoice?” -
for example vetting a supplier before paying its first invoice. The classic
company-data APIs sit behind a subscription and an account an agent cannot
sign up for; here it is one paid call, no account. Lookup is by SIREN or
SIRET only; there is no search by name in v1 - the endpoint serves agents
that already hold an identifier (from an invoice, a contract, a VAT number).
See the live /catalog for the authoritative endpoint
listing and price.
x402 golden rule: the agent pays for the answer to its question. A
company found in the register is a successful answer -> 200 billed, even
when the company has ceased - status: "ceased" is the information the
agent came for. Requests the service cannot answer - an identifier unknown to
the register, a malformed one, a failed checksum - leave the 200 range and
are not billed.
Parameters
Exactly one of siren or siret, as a query parameter. Providing both,
or neither, is a 400 INVALID_INPUT.
| Parameter | Type | Required | Description |
|---|---|---|---|
siren | string | one of the two | 9-digit legal-unit identifier |
siret | string | one of the two | 14-digit establishment identifier (SIREN + 5-digit NIC) |
GET /company/resolve?siren=394406938
Both identifiers are validated in two steps: strict form (length, digits
only), then the Luhn check digit - with one exception: SIRETs on the
La Poste prefix 356000000 carry no check-digit control at all. The
“sum-of-digits-multiple-of-5” rule long attributed to them is demonstrably
wrong on INSEE’s own data: 41 of the 13,121 real La Poste establishments in
the 2026-06-30 stock fail it - including the head office 35600000000048
that this same endpoint serves in headquarters.siret (a broken round-trip).
So the checksum is bypassed entirely for that prefix, and a well-formed but
unknown La Poste SIRET is a 404 NOT_FOUND (the existence index decides),
never a 400 INVALID_CHECKSUM. There is no exception on the SIREN side -
356000000 itself passes the standard Luhn check.
For every other identifier, a well-formed value that fails its checksum is a
400 INVALID_CHECKSUM, distinct from a 404: it is almost certainly a typo,
and the agent should fix the input rather than conclude the company does not
exist.
200 response - UnifiedResponse
{
"data": { ... },
"provenance": {
"source": "insee-sirene",
"fetched_at": "2026-07-11T18:36:02Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-30T00:00:00Z" }
}
}
provenance.source:insee-sirene, the served Sirene store.freshness.kind:snapshot;as_ofis the end-of-month date of the Sirene stock image that backed the answer.
Fields of data
| Field | Type | Description |
|---|---|---|
siren | string | The resolved legal unit’s SIREN |
name | string | Legal name (denomination); null for a natural person - see natural_person |
acronym | string | Registered acronym (sigle); null when none |
natural_person | object | { last_name, first_name } when the legal unit is a natural person (sole trader); else null |
status | string | active or ceased - administrative status of the legal unit |
legal_form | object | { code, label } - INSEE legal-category code and its official label |
main_activity | object | { code, label, nomenclature } - NAF/APE activity code, label and nomenclature (NAFRev2) |
workforce | object | { bracket, year } - INSEE workforce band code and its reference year |
created_on | string | Creation date of the legal unit (YYYY-MM-DD) |
last_processed_on | string | Date the record was last processed by INSEE |
is_employer | bool | Employer flag as recorded by INSEE; null when not recorded |
social_economy | bool | Whether the company belongs to the social and solidarity economy (ESS) |
headquarters | object | Head-office establishment: { siret, status, address { line, postal_code, city, country }, created_on }; address.country: null means France |
is_headquarters | bool | Present only for a SIRET input: whether the queried SIRET is the head office |
lei | object | Present only when at least one LEI declares this SIREN - the link to the global GLEIF identity, see below |
coverage | object | What the register could, or could not, provide for this record |
The legal_form.label and main_activity.label values come from the
embedded INSEE nomenclatures; a code outside the embedded referential is
served with label: null, never an invented label.
coverage
| Field | Type | Description |
|---|---|---|
missing | string[] | JSON paths absent from the register for this record (partial/protected diffusion, or simply not filled in) - served null, never filled in |
establishment | string | Present as "resolved_to_legal_unit" when the input was the SIRET of a secondary establishment - see below |
lei - the link to the global identity
When the GLEIF LEI reference carries an entity declaring this SIREN, the answer
adds a lei block. It is absent for the vast majority of French companies
(169,049 SIREN bridged at the 2026-07-28 GLEIF image, out of ~3M legal units) -
that is a fact, not a gap: a small company has no reason to hold a LEI.
| Field | Type | Description |
|---|---|---|
total_matches | number | Always told: how many LEIs declare this SIREN |
lei | string | The LEI - populated only when there is exactly one |
legal_name | string | The GLEIF legal name - same condition |
entity_status | string | GLEIF entity status - same condition |
registration_status | string | GLEIF registration status (ISSUED, LAPSED…) - same condition |
verified_against_sirene | bool | Whether the declared SIREN exists in the Sirene register; null if unverified |
The shape is stable: total_matches always says how many, and the identity
fields are filled in only when there is exactly one. Beyond one (176 SIREN,
at most 3 LEIs each - typically an active record plus a DUPLICATE) they are
null: picking would be the guesswork this catalog refuses, and the full list
is one call away on
GET /company/lei?siren=.
legal_name is the GLEIF name, served beside the Sirene name, never in
its place: two self-declared registers, two truths, neither rewritten with the
other. The response’s provenance stays insee-sirene - the identity served
is the register’s; the block carries a link, not a second source.
Per-block provenance is what GET /company/report
does.
If the GLEIF dataset is absent, or predates the SIREN bridge, the block is simply absent and the route is otherwise unchanged: the link is a bonus, never a dependency.
Example - active company, resolved by SIREN
GET /company/resolve?siren=394406938, against the 2026-06-30 Sirene
snapshot:
{
"data": {
"siren": "394406938",
"name": "BOULANGERIE AURELIA",
"acronym": null,
"natural_person": null,
"status": "active",
"legal_form": {
"code": "5710",
"label": "SAS, société par actions simplifiée"
},
"main_activity": {
"code": "10.71C",
"label": "Boulangerie et boulangerie-pâtisserie",
"nomenclature": "NAFRev2"
},
"workforce": {
"bracket": "21",
"year": 2023
},
"created_on": "1994-04-01",
"last_processed_on": "2025-12-06",
"is_employer": null,
"social_economy": false,
"headquarters": {
"siret": "39440693800149",
"status": "active",
"address": {
"line": "519 AVENUE VICTOR HUGO",
"postal_code": "83600",
"city": "FREJUS",
"country": null
},
"created_on": "2021-03-31"
},
"coverage": {
"missing": []
}
},
"provenance": {
"source": "insee-sirene",
"fetched_at": "2026-07-11T18:36:02Z",
"freshness": {
"kind": "snapshot",
"as_of": "2026-06-30T00:00:00Z"
}
}
}
The example above carries no lei block - BOULANGERIE AURELIA holds no
LEI, like most French companies. A company that does gets it:
GET /company/resolve?siren=542051180 (extract, same 2026-06-30 Sirene
snapshot, 2026-07-28 GLEIF image):
{
"siren": "542051180",
"name": "TOTALENERGIES SE",
"status": "active",
"…": "…",
"lei": {
"lei": "529900S21EQ1BO4ESM68",
"legal_name": "TotalEnergies SE",
"entity_status": "ACTIVE",
"registration_status": "ISSUED",
"verified_against_sirene": true,
"total_matches": 1
},
"coverage": { "missing": [] }
}
A ceased company is a paid answer
status: "ceased" is returned as a 200 and billed: the agent asked “who
is this company and is it still active?”, and “it has ceased” is precisely
the answer that should stop a payment or an onboarding. A real example: SIREN
552100554 (PEUGEOT SA) resolves with status: "ceased". The service never
turns a ceased status into an error.
SIRET input: resolution to the legal unit
The v1 identity is the legal unit plus its head office. When the input is a SIRET:
- The existence of the establishment is checked against an index built
from the full official establishment stock. A Luhn-valid SIRET whose
establishment does not exist in the register is a
404 NOT_FOUND, not billed - with a 5-digit NIC, a typo has roughly a 1-in-10 chance of passing the checksum, and existence is what catches it. - A SIRET that exists and is the head office resolves normally, with
is_headquarters: true. - A SIRET of an existing secondary establishment (even a ceased one)
resolves to its parent legal unit, with
is_headquarters: falseandcoverage.establishment: "resolved_to_legal_unit". Thedatadescribes the legal unit and its head office - not the queried establishment’s own record (address, dates, workforce), which the v1 store does not carry. The marker tells the agent exactly how far the answer goes.
Coverage honesty
The Sirene register itself has gaps: some records are under partial or
protected diffusion, and some fields are simply not filled in. A field the
register does not provide is served as null and listed in
coverage.missing (for example workforce or headquarters.address) -
never filled in from another source, never guessed. Labels come from the
embedded INSEE nomenclatures or are null; the service does not invent
what the register does not say.
Scope is deliberately bounded to the official register: no search by name, no scoring, no enrichment from unofficial sources.
Freshness
Answers are served from a monthly snapshot of the Sirene stock;
freshness.as_of carries the end-of-month date of the image that backed the
answer. If a monthly refresh is missed, the service keeps serving the last
good snapshot - honestly dated by as_of - and never turns staleness into a
5xx.
Errors
Only requests the service cannot answer leave the 200 range - none of them are billed.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_INPUT | Malformed identifier (wrong length, non-digits), or both/neither of siren and siret provided |
| 400 | INVALID_CHECKSUM | Well-formed identifier that fails its Luhn check - a probable typo the agent should fix |
| 404 | NOT_FOUND | Well-formed, checksum-valid identifier unknown to the register (including a Luhn-valid SIRET whose establishment does not exist); also unknown routes |
| 503 | DATA_UNAVAILABLE | The store has not been ingested yet - there is no last good snapshot to serve |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "provide exactly one of 'siren' or 'siret'", "code": "INVALID_INPUT" }
{ "error": "invalid checksum for siren `394406939`", "code": "INVALID_CHECKSUM" }
{ "error": "siren `000000018` not found in the Sirene register", "code": "NOT_FOUND" }
Attribution
Company identity data is derived from the Sirene register, published by INSEE (the French national institute of statistics) under the Licence Ouverte / Open Licence (Etalab).
See also
GET /company/lei- the same question outside France: legal identity from a LEI, BIC, ISIN, SIREN or exact name plus country, from the global GLEIF reference.GET /company/ownership- who consolidates this entity: direct parent, ultimate parent, direct subsidiaries.GET /iban/screen- screen a counterparty’s IBAN against the OFAC/EU/UN sanctions lists before paying it.GET /phone/validate- normalize and validate a counterparty’s phone number offline.- For agents - discovery surfaces, the live
/catalogand how settlement works.