Is this SIREN a real, still-active company? INSEE Sirene
An agent holding a SIREN or SIRET gets the official INSEE Sirene identity card in one paid call — legal name, active or ceased status, head office, no account.
By Matthias Begot · · Updated
An agent about to onboard a supplier, book an inbound invoice or release a
first payment holds a nine-digit number it did not verify. 394406938 is a
well-formed SIREN. So is 394406939 — it just isn’t one.
GET /company/resolve turns that number into the
official identity card: legal name, active or ceased status, legal form,
NAF activity, workforce band, creation date and head-office address, served
offline from the INSEE Sirene register under the Etalab open licence. One
call, no account, no API key.
The three things worth knowing before wiring it are that a ceased company is
a paid answer, that a SIRET resolves to its legal unit and says so, and
that a field the register does not carry comes back null with a receipt
rather than filled in from somewhere else. Below, each is walked with responses
captured from production on 11 August 2026, against the Sirene snapshot dated
2026-07-31.
The identity card, captured
GET /company/resolve?siren=542051180:
{
"data": {
"siren": "542051180",
"name": "TOTALENERGIES SE",
"acronym": null,
"natural_person": null,
"status": "active",
"legal_form": { "code": "5800", "label": "Société européenne" },
"main_activity": {
"code": "70.10Z",
"label": "Activités des sièges sociaux",
"nomenclature": "NAFRev2"
},
"workforce": { "bracket": "51", "year": 2023 },
"created_on": "1954-01-01",
"last_processed_on": "2025-12-06",
"is_employer": null,
"social_economy": false,
"headquarters": {
"siret": "54205118000066",
"status": "active",
"address": {
"line": "2 PLACE JEAN MILLIER",
"postal_code": "92400",
"city": "COURBEVOIE",
"country": null
},
"created_on": "2000-03-22"
},
"lei": {
"lei": "529900S21EQ1BO4ESM68",
"legal_name": "TotalEnergies SE",
"entity_status": "ACTIVE",
"registration_status": "ISSUED",
"verified_against_sirene": true,
"total_matches": 1
},
"coverage": { "missing": [] }
},
"provenance": {
"source": "insee-sirene",
"fetched_at": "2026-08-11T15:21:50Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-31T00:00:00Z" }
}
}
The lookup runs against a local store, so there is no network call and no
secret at request time — the same property that makes
/iban/resolve and
/phone/validate answer in milliseconds. The key
is a SIREN or a SIRET, never a name: this endpoint serves agents that
already hold an identifier off an invoice, a contract or a supplier form.
The lei block appears only when an entity in the GLEIF reference declares
this SIREN — a bridge to the global identity, and the entry point to
GET /company/lei. It is absent for most French
companies, which is a fact about LEIs, not a gap. Note that the GLEIF
legal_name sits beside the Sirene name, never in place of it: two
self-declared registers, two spellings, neither rewritten with the other.
Roughly half of what you can look up has ceased
status: "ceased" comes back as a 200, and it is billed. That is the
whole point: the agent asked “who is this company and is it still trading?”,
and “it stopped” is precisely the answer that should halt an onboarding.
GET /company/resolve?siren=552100554 returns PEUGEOT SA with
status: "ceased", a ceased head office in Vélizy-Villacoublay, and a lei
block reading entity_status: "INACTIVE", registration_status: "RETIRED".
Two registers agreeing that a famous name is no longer a live counterparty.
This is not an edge case. Probing random check-digit-valid SIRENs against the
served snapshot, 80 of the 169 legal units that resolved came back
ceased — the register is a historical record, not a directory of going
concerns. An integration that treats “found in Sirene” as “safe to pay” has
misread the answer by a factor of two.
The register also moves in bulk. INSEE ran a consistency alignment between
Sirene and the national business registers on 17–18 June 2026, updating
over 260,000 legal units and their establishments, in two successive
waves, principally cessations and closures
(Insee, Sirene Open Data — actualités).
A company an agent resolved as active in May could be correctly ceased in
the July snapshot without anything having “broken”.
A SIRET answers about the legal unit, and admits it
The v1 identity is the legal unit plus its head office. Hand it a SIRET and three things can happen, each distinguishable without guessing:
| Input | Result |
|---|---|
| SIRET of the head office | Normal answer plus is_headquarters: true |
| SIRET of an existing secondary establishment | Answer about the parent legal unit, is_headquarters: false, plus coverage.establishment: "resolved_to_legal_unit" |
| Luhn-valid SIRET whose establishment does not exist | 404 NOT_FOUND, not billed |
GET /company/resolve?siret=54205118000033 — a secondary TotalEnergies
establishment — returns the TotalEnergies legal unit with:
"is_headquarters": false,
"coverage": { "establishment": "resolved_to_legal_unit", "missing": [] }
Read that marker before reading the address. The data describes the legal
unit and its head office, not the queried establishment’s own record — the
v1 store does not carry per-establishment addresses, dates or workforce, and
says so rather than quietly serving the head office as though it were the
branch on the invoice.
The existence check matters more than it looks. A SIRET’s five-digit NIC means a typo has roughly a one-in-ten chance of passing the checksum; the existence index is what catches the nine other cases.
Checksum failure and non-existence are different answers
Both leave the 200 range, both are unbilled, and an agent must react to them in opposite ways:
GET /company/resolve?siren=394406939
→ 400 { "code": "INVALID_CHECKSUM", "error": "siren failed its checksum (Luhn): likely a typo" }
GET /company/resolve?siren=000000018
→ 404 { "code": "NOT_FOUND", "error": "siren not found in the Sirene register" }
The first says fix your input — one digit off a real number. The second says this key is well-formed and the register has never heard of it, which is the answer for an ERP filler value or a fabricated identifier. Collapsing the two into “lookup failed” throws away the distinction that tells an agent whether to retry or to stop.
There is one documented exception, and it is live: SIRETs on the La Poste
prefix 356000000 carry no check-digit control at all. 35600000000051
fails the standard Luhn test and is nonetheless a real establishment — it
resolves 200 to LA POSTE with resolved_to_legal_unit. So the checksum is
bypassed for that prefix entirely and existence decides alone:
35600000099999 is a 404, never a 400. A rule that would reject genuine
identifiers is not a rule worth enforcing.
What the register does not say, it does not invent
The Sirene register has real gaps: records under partial or protected
diffusion, fields simply never filled in. Those are served as null and
listed in coverage.missing. A live example, an active sole trader:
{
"siren": "810678904",
"name": null,
"natural_person": null,
"status": "active",
"legal_form": { "code": "1000", "label": "Entrepreneur individuel" },
"main_activity": { "code": "68.20A", "label": "Location de logements", "nomenclature": "NAFRev2" },
"headquarters": {
"siret": "81067890400030",
"status": "active",
"address": { "line": null, "postal_code": null, "city": "PARIS", "country": null }
},
"coverage": { "missing": ["name", "headquarters.address"] }
}
The company exists, it is active, and its name and street address are
withheld by the register — not missing from the service. An agent reading
coverage.missing knows the difference between “no data” and “no answer”, and
can escalate to a human instead of concluding the supplier is fake. In the
same sample of 169 units, headquarters.address was withheld or unfilled 22
times and name 13 times.
Labels follow the same discipline. legal_form.label and
main_activity.label come from embedded INSEE nomenclatures; a code
outside them is served with label: null, never an invented wording. That is
common on old records: of the 169 sampled, 123 carried an activity code in
NAFRev2, but 25 were still in NAF1993, 10 in NAFRev1 and 11 in the
1970s-era NAP — 46 of them with a null label. Which is exactly why
main_activity.nomenclature is a field and not a footnote.
Store the nomenclature, because it is about to change
From 1 January 2027, NAF 2025 becomes the reference nomenclature for APE codes, approved by décret n° 2025-736 du 31 juillet 2025. INSEE is explicit about the blast radius: at that date every active legal unit in the Sirene register and its establishments will have a modified APE code (Insee, Vers une nouvelle nomenclature : la NAF 2025).
An agent that stored "70.10Z" alone has stored something that will silently
mean a different thing. An agent that stored code, nomenclature and
provenance.freshness.as_of together can still explain, in 2028, what it knew
and when.
Billing and failure modes
| Case | Result |
|---|---|
Company found — including ceased | 200, billed |
| SIRET of a secondary establishment, resolved to its legal unit | 200, billed |
Fields withheld by the register, listed in coverage.missing | 200, billed |
Both or neither of siren / siret, malformed identifier | 400 INVALID_INPUT, not billed |
| Well-formed identifier failing its Luhn check | 400 INVALID_CHECKSUM, not billed |
| Well-formed identifier unknown to the register | 404 NOT_FOUND, not billed |
| Store not yet ingested | 503 DATA_UNAVAILABLE, not settled |
The line is the same one the rest of the catalog draws: the agent pays for the answer to its question, and an absence of activity is an answer. A key the register has never seen is not — nobody should pay to learn their input was fiction.
Freshness is declared, never faked. Answers carry
freshness.kind: "snapshot" with the end-of-month as_of of the Sirene stock
that backed them; INSEE publishes a monthly stock file, and a missed refresh
means the last good snapshot keeps being served under its honest date. Stale
data is never turned into a 5xx.
Wiring it up
- Branch on
statusbefore anything else.activeandceasedare both200s. Only one of them should let a payment through. - Check
coveragebefore trusting a null.missingtells you the register withheld a field;establishmenttells you a SIRET was resolved upward to its legal unit. - Keep
freshness.as_ofwith the record. “On what basis did the agent onboard this supplier in August 2026” is answerable only if the snapshot date was stored beside the verdict. - Treat 400 and 404 as distinct branches. Retry after fixing the input;
stop after a
NOT_FOUND. - Discover, then pay. The
402→ pay → replay cycle is walked with runnable snippets in the Quickstart; the machine discovery surfaces are described in For agents.
Identity is one question among several. The SIREN you just resolved is also
the key to /company/vat (is the intra-EU VAT number
valid today), /company/events (has an insolvency
announcement been published), /company/peppol
(can this company actually receive the e-invoice you are about to send — which
becomes an operational question for every VAT-registered French business from
1 September 2026, when receiving structured e-invoices becomes mandatory),
and /company/ownership (who consolidates it).
If you would rather buy the whole picture in a single settlement instead of
orchestrating the family yourself,
GET /company/report bundles them by SIREN with
provenance per block, and
/preflight/supplier turns the bundle into a
one-call onboarding verdict. The identity of an issuer read off an inbound
invoice starts at /invoice/read.
Every amount and every accepted settlement rail lives in the gateway’s
/catalog, which is the single source of
truth — no price appears in this article.
Two limits to keep in view. There is no search by name: the key is an
identifier, and no fuzzy matching will be added, because a near-match wearing
an official register’s authority is a guess. And this is identity, not
risk — no score, no rating, no enrichment from unofficial sources. Naming a
counterparty is not screening it; that is
/screen/entity for the name and
/iban/screen for the account.
The captures above are snapshot-dated illustrations, not a contract: a
monthly refresh moves a status, a diffusion request withholds a name, an
establishment closes. Only the rules are guaranteed — one key per call, a
ceased company served as a ceased company, a withheld field named rather than
invented, and an unanswerable request never billed. The full parameter and
field contract is on the
GET /company/resolve documentation.