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.

ParameterTypeRequiredDescription
sirenstringone of the two9-digit legal-unit identifier
siretstringone of the two14-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_of is the end-of-month date of the Sirene stock image that backed the answer.

Fields of data

FieldTypeDescription
sirenstringThe resolved legal unit’s SIREN
namestringLegal name (denomination); null for a natural person - see natural_person
acronymstringRegistered acronym (sigle); null when none
natural_personobject{ last_name, first_name } when the legal unit is a natural person (sole trader); else null
statusstringactive or ceased - administrative status of the legal unit
legal_formobject{ code, label } - INSEE legal-category code and its official label
main_activityobject{ code, label, nomenclature } - NAF/APE activity code, label and nomenclature (NAFRev2)
workforceobject{ bracket, year } - INSEE workforce band code and its reference year
created_onstringCreation date of the legal unit (YYYY-MM-DD)
last_processed_onstringDate the record was last processed by INSEE
is_employerboolEmployer flag as recorded by INSEE; null when not recorded
social_economyboolWhether the company belongs to the social and solidarity economy (ESS)
headquartersobjectHead-office establishment: { siret, status, address { line, postal_code, city, country }, created_on }; address.country: null means France
is_headquartersboolPresent only for a SIRET input: whether the queried SIRET is the head office
leiobjectPresent only when at least one LEI declares this SIREN - the link to the global GLEIF identity, see below
coverageobjectWhat 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

FieldTypeDescription
missingstring[]JSON paths absent from the register for this record (partial/protected diffusion, or simply not filled in) - served null, never filled in
establishmentstringPresent as "resolved_to_legal_unit" when the input was the SIRET of a secondary establishment - see below

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.

FieldTypeDescription
total_matchesnumberAlways told: how many LEIs declare this SIREN
leistringThe LEI - populated only when there is exactly one
legal_namestringThe GLEIF legal name - same condition
entity_statusstringGLEIF entity status - same condition
registration_statusstringGLEIF registration status (ISSUED, LAPSED…) - same condition
verified_against_sireneboolWhether 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.

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: false and coverage.establishment: "resolved_to_legal_unit". The data describes 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.

StatuscodeCase
400INVALID_INPUTMalformed identifier (wrong length, non-digits), or both/neither of siren and siret provided
400INVALID_CHECKSUMWell-formed identifier that fails its Luhn check - a probable typo the agent should fix
404NOT_FOUNDWell-formed, checksum-valid identifier unknown to the register (including a Luhn-valid SIRET whose establishment does not exist); also unknown routes
503DATA_UNAVAILABLEThe store has not been ingested yet - there is no last good snapshot to serve
500INTERNALInternal 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 /catalog and how settlement works.