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.
| Parameter | Type | Required | Description |
|---|---|---|---|
cis | string | one of the four | French CIS code - exactly 8 digits (BDPM) |
name | string | one of the four | Exact product name - French implicit, no country here (generic groups are a BDPM notion) |
dci | string | one of the four | INN/DCI substance name, resolved through the shared canonical substance key (dci=acetaminophen finds the paracetamol groups) |
ndc | string | one of the four | US 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) | source | freshness.kind |
|---|---|---|
cis, name, dci | BDPM | snapshot - monthly BDPM image |
ndc | medication-grid generics coverage (BDPM scope) | live - pure local scope statement, no store consulted |
Fields of data
| Field | Type | Description |
|---|---|---|
found | bool | false with groups: [] when the key is well-formed but carries no group - a billed 200 |
groups | object[] | The generic groups the key belongs to (see below); a cis may belong to several, a name/dci may aggregate several |
coverage | object | Present 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
| Field | Type | Description |
|---|---|---|
id | int | BDPM generic-group id |
label | string | Official BDPM group label (names the reference princeps) |
members | object[] | The medications in the group, in BDPM order (see below) |
A members[] entry
| Field | Type | Description |
|---|---|---|
cis | string | The member’s French CIS code |
name | string | Official product name - omitted when the member CIS is not in the specialities register, never guessed |
identity | string | Why the identity is missing - withdrawn_from_bdpm_specialities; present only when name is omitted (see below) |
type | string | BDPM role - one of princeps / generique / complement_posologique / substituable (closed set) |
is_query | bool | true on the member(s) matching the queried key |
presentations | object[] | Boxes with price and reimbursement rate (see below); omitted when none is published |
A presentations[] entry
| Field | Type | Description |
|---|---|---|
cip7 / cip13 | string | CIP presentation codes |
label | string | Packaging label |
price_cents | int | Public price in euro cents (integer, never a monetary float); omitted when the price is free/unrecorded |
reimbursement_rate | string | UNCAM rate as published (e.g. "65%"); omitted when not reimbursed/unrecorded |
collectivities_approved | bool | Approved 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
coveragenote:
"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.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_INPUT | Zero or several keys; a malformed key (e.g. a cis that is not 8 digits, a package NDC); an empty name/dci |
| 503 | DATA_UNAVAILABLE | The BDPM register has not been ingested yet - there is no last good snapshot to serve (cis/name/dci only) |
| 404 | NOT_FOUND | Unknown route |
| 500 | INTERNAL | Internal 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
/catalogand how settlement works.