GET /medication/generics

Returns the generic group(s) a French medication belongs to, from the BDPM register (ANSM/HAS): the reference brand-name drug (princeps) and every equivalent generic, each with its BDPM role, its official name and its presentations - public price in cents and reimbursement rate as published. The answer is served from a local store built from the official BDPM dataset, so a lookup resolves in milliseconds with no network call and no secret at request time.

Use it when an agent needs to check substitution or sourcing before acting on a prescription or an order - “what is the reference drug behind this generic?”, “which generics are equivalent to this brand, and at what public price?”, “is this medication even part of a generic group?”. It extends the “pre-action checks” angle on the prescription side, next to /medication/resolve. Lookup is by identifier, exact name or substance only - there is no search by symptom, indication or fuzzy name in v1. See the live /catalog for the authoritative endpoint listing and price.

Generic groups are a French (BDPM) notion. The scope of this endpoint is FR: a US ndc key returns an honest coverage note, never an error, and no US equivalent is promised.

This endpoint returns sourced, dated facts - BDPM roles, prices and rates exactly as published. It is never a substitution recommendation, clinical advice, or a patient dosage. That line is the boundary of the service.

x402 golden rule: the agent pays for the answer to its question. A well-formed key that carries no generic group is a successful answer too -> 200 billed with data.found: false and data.groups: [] - the absence of a group is the information. Only requests the service cannot answer - no key, several keys, a malformed key, an empty name - leave the 200 range and are not billed.

Parameters

Exactly one of the four keys, as a query parameter. Zero or several keys, a malformed key, or an empty name/dci is a 400 INVALID_INPUT.

ParameterTypeRequiredDescription
cisstringone of the fourFrench CIS code - exactly 8 digits (BDPM)
namestringone of the fourExact product name - French implicit, no country here (generic groups are a BDPM notion)
dcistringone of the fourINN/DCI substance name, resolved through the shared canonical substance key (dci=acetaminophen finds the paracetamol groups)
ndcstringone of the fourUS product NDC labeler-product (e.g. 0002-3227) - out of FR scope, returns a coverage note
GET /medication/generics?cis=60887980

Unlike /medication/resolve, a name here takes no country: generic groups are a French concept, so the name is resolved against the French register only. Name resolution folds case, accents and punctuation and may aggregate several groups. A dci is resolved through the shared canonical substance key, so an English INN reaches the French groups of the same molecule.

200 response - UnifiedResponse

{
  "data": { ... },
  "provenance": {
    "source": "BDPM",
    "fetched_at": "2026-07-19T18:45:08Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-18T00:00:00Z" }
  }
}

provenance depends on the key that resolved the answer:

Key(s)sourcefreshness.kind
cis, name, dciBDPMsnapshot - monthly BDPM image
ndcmedication-grid generics coverage (BDPM scope)live - pure local scope statement, no store consulted

Fields of data

FieldTypeDescription
foundboolfalse with groups: [] when the key is well-formed but carries no group - a billed 200
groupsobject[]The generic groups the key belongs to (see below); a cis may belong to several, a name/dci may aggregate several
coverageobjectPresent in two cases only, never on a nominal answer: an ndc key -> { scope: "FR", note }, the honest FR-scope statement; a group holding members with no identity -> { members_without_identity, note } (see below)

A field the source does not carry is omitted from the JSON, never served null.

A groups[] entry

FieldTypeDescription
idintBDPM generic-group id
labelstringOfficial BDPM group label (names the reference princeps)
membersobject[]The medications in the group, in BDPM order (see below)

A members[] entry

FieldTypeDescription
cisstringThe member’s French CIS code
namestringOfficial product name - omitted when the member CIS is not in the specialities register, never guessed
identitystringWhy the identity is missing - withdrawn_from_bdpm_specialities; present only when name is omitted (see below)
typestringBDPM role - one of princeps / generique / complement_posologique / substituable (closed set)
is_querybooltrue on the member(s) matching the queried key
presentationsobject[]Boxes with price and reimbursement rate (see below); omitted when none is published

A presentations[] entry

FieldTypeDescription
cip7 / cip13stringCIP presentation codes
labelstringPackaging label
price_centsintPublic price in euro cents (integer, never a monetary float); omitted when the price is free/unrecorded
reimbursement_ratestringUNCAM rate as published (e.g. "65%"); omitted when not reimbursed/unrecorded
collectivities_approvedboolApproved for use by public bodies

Roles, prices and rates are relayed exactly as published - they are facts, not a recommendation to substitute one member for another.

Example - a generic, resolved by CIS

GET /medication/generics?cis=60887980 (a clomipramine generic), against the 2026-07-18 BDPM snapshot:

{
  "data": {
    "found": true,
    "groups": [
      {
        "id": 277,
        "label": "CLOMIPRAMINE (CHLORHYDRATE DE) 10 mg - ANAFRANIL 10 mg, comprimé enrobé",
        "members": [
          {
            "cis": "69273510",
            "name": "ANAFRANIL 10 mg, comprimé enrobé",
            "type": "princeps",
            "is_query": false
          },
          {
            "cis": "60887980",
            "identity": "withdrawn_from_bdpm_specialities",
            "type": "generique",
            "is_query": true,
            "presentations": [
              {
                "cip7": "3689261",
                "cip13": "3400936892613",
                "label": "plaquette(s) PVC-Aluminium PVDC de 60 comprimé(s)",
                "marketing": "Déclaration d'arrêt de commercialisation",
                "declared_at": "2024-12-10",
                "collectivities_approved": false,
                "reimbursement_rate": "65%",
                "price_cents": 210
              }
            ]
          }
        ]
      }
    ],
    "coverage": {
      "members_without_identity": 1,
      "note": "1 group member(s) have no name or presentations: the BDPM generic-groups file references CIS codes absent from the BDPM specialities file (marketing authorisation withdrawn); nothing is inferred"
    }
  },
  "provenance": {
    "source": "BDPM",
    "fetched_at": "2026-07-19T18:45:08Z",
    "freshness": { "kind": "snapshot", "as_of": "2026-07-18T00:00:00Z" }
  }
}

The queried generic member carries is_query: true but no name (its CIS is outside the specialities register of this snapshot - never guessed), so it carries identity and the answer counts it in coverage (see the next section); the princeps carries no presentations (none published -> the field is omitted). A group can hold more members than shown here, and a cis/name/dci can return several groups.

A member with no identity says so

The BDPM publishes generic groups and specialities in two separate files, and the groups file references CIS codes that the specialities file no longer carries (marketing authorisation withdrawn and not carried over) - about 24 % of all group members. Such a member is served with its cis, its BDPM type and nothing else: no name, no presentations.

The endpoint never leaves that silent, and never invents the missing identity:

  • the member carries identity: "withdrawn_from_bdpm_specialities", so the agent knows the gap is in the source, not in the service;
  • the answer carries a counted coverage note:
"coverage": {
  "members_without_identity": 1,
  "note": "1 group member(s) have no name or presentations: the BDPM generic-groups file references CIS codes absent from the BDPM specialities file (marketing authorisation withdrawn); nothing is inferred"
}

Those members are kept in members[]: they belong to the group, and dropping them would misstate its composition. A group whose members are all known carries no coverage at all. The same marking appears in the generics section of /medication/report.

A well-formed but ungrouped key is a paid answer

GET /medication/generics?cis=99999999 - a well-formed CIS with no generic group, or an unknown CIS - returns a 200, billed, with data.found: false and data.groups: []. The agent asked “is this medication part of a generic group?”, and “no group” is the answer. Contrast this with the 400s below, which are malformed requests and are not billed.

A US NDC is honest coverage, not an error

GET /medication/generics?ndc=0002-3227 returns a 200, billed, with data.found: false, data.groups: [] and:

"coverage": {
  "scope": "FR",
  "note": "generic groups are a French (BDPM) notion; NDC keys are out of scope in v1"
}

provenance.source is "medication-grid generics coverage (BDPM scope)" and freshness.kind is "live": it is a pure local scope statement, consulting no store (so an ndc key never returns a 503). There is no US generic-group equivalent in v1 - the endpoint says so honestly rather than guessing.

Freshness

Answers are served from a monthly snapshot of the BDPM register; freshness.as_of carries the 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. A register not yet ingested at all is a 503 DATA_UNAVAILABLE (there is no last good snapshot to serve) for the cis/name/dci keys; an ndc key depends on no store and never returns 503.

Errors

Only requests the service cannot answer leave the 200 range - none of them are billed.

StatuscodeCase
400INVALID_INPUTZero or several keys; a malformed key (e.g. a cis that is not 8 digits, a package NDC); an empty name/dci
503DATA_UNAVAILABLEThe BDPM register has not been ingested yet - there is no last good snapshot to serve (cis/name/dci only)
404NOT_FOUNDUnknown route
500INTERNALInternal error (detail logged, not exposed)
{ "error": "provide exactly one of: cis, name, dci, ndc", "code": "INVALID_INPUT" }

Attribution

French medication data is derived from the BDPM (Base de données publique des médicaments), made freely available under article L.161-40-1 of the French Social Security Code (source and date required).

See also

  • GET /medication/resolve - the official identity card of a medication (status, substances, price) before checking its generic group.
  • GET /company/resolve - resolve a French company from its SIREN or SIRET before onboarding a counterparty.
  • For agents - discovery surfaces, the live /catalog and how settlement works.