GET /label/claims/permitted
Lists the authorised health claims for a nutrient or substance against the EU Register of nutrition and health claims (Regulation (EC) 1924/2006). For a nutrient, the answer is the set of claims an agent may legally write — the exact legal wording of each authorised claim, with its conditions of use as written and the referenced EFSA opinion. The register is served from a local store, so a lookup resolves in milliseconds with no network call and no secret at request time.
This is the catalogue counterpart of /label/claims/check:
check starts from a wording (“is this text legal?”), permitted starts from
a nutrient (“what may I write?”). Both read the same dated register. Use
permitted when an agent needs to build a compliant label — the natural
sequel to /label/chem/classify and
/label/chem/svhc. The register is binding to the
character: permitted serves the legal wording as published, not a paraphrase
— where an LLM invents a plausible-sounding but non-conforming claim, this
endpoint answers from the register as published, dated and sourced. See the
live /catalog for the authoritative endpoint listing and
price.
This endpoint relays the register content exactly as published — the legal wording and the conditions of use as written. It is never legal advice, nor a label-copy recommendation — it lists the authorised claims of a nutrient, it does not decide what your specific label should say. That line is the boundary of the service.
x402 golden rule: the agent pays for the answer to its question. “Which
claims are authorised for this nutrient?” is a well-formed question, and an empty
list is the information → 200 billed even when a nutrient has no authorised
claim, or is not in the register at all — never a 404. Only requests the service
cannot answer — a missing or empty nutrient — leave the 200 range and are not
billed.
Request
GET with a required query parameter — the lookup key is short, so it is passed in
the query string (unlike /label/claims/check,
which takes a long wording in a POST body).
GET /label/claims/permitted?nutrient=vitamin%20c
| Parameter | Type | Required | Description |
|---|---|---|---|
nutrient | string | yes | The nutrient / substance whose authorised claims you want. Absent, empty, or empty after trim → 400 MISSING_PARAMETER, not billed |
The key is normalised (case, whitespace) before lookup; data.nutrient returns
the canonical form actually queried. There is deliberately no fuzzy search
(a scoping decision): the nutrient must exist in the register under its normalised
form, otherwise the verdict is unknown_nutrient.
200 response — UnifiedResponse
{
"data": { "nutrient": "vitamin c", "verdict": "found", "authorised": [ ... ], "non_authorised_count": 0 },
"provenance": {
"source": "EU Register on nutrition and health claims",
"fetched_at": "2026-07-24T09:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-23T00:00:00Z" }
}
}
freshness.as_of is dated by the extraction of the register served
(meta.json), not by the ingestion date — the dated register is the point of the
answer. Both claims endpoints (check and permitted) read the same store, so
they share the same as_of.
Fields of data
| Field | Type | Description |
|---|---|---|
nutrient | string | The normalised (canonical) key actually queried |
verdict | string | found | none_authorised | unknown_nutrient — all three are billed 200s |
authorised | object[] | The authorised claims of the nutrient as published (see below); empty on none_authorised and unknown_nutrient |
non_authorised_count | number | The count of non-authorised claims of the nutrient — explains an empty authorised list; 0 on unknown_nutrient |
note | string | unknown_nutrient only — the dated contractual limit note (see below) |
A field the source does not carry is omitted from the JSON, never served
null.
The three verdicts
All three are 200 billed — the golden rule: an empty list of authorised claims is the answer, never a 404.
found— the nutrient is in the register with at least one authorised claim (authorised[]is non-empty).none_authorised— the nutrient is in the register but has no authorised claim (all rejected):authorised: [], explained bynon_authorised_count > 0. The empty list is honestly accounted for, not a silent omission — the nutrient exists, its claims were all refused.unknown_nutrient— the nutrient is absent from the register:authorised: [],non_authorised_count: 0, plus anote. This is not “no claim is possible”, never a 404.
non_authorised_count is the honest distinction between the two empty-list cases:
a positive count means none_authorised (a known nutrient, everything rejected);
0 with unknown_nutrient means the nutrient is simply not in the register.
An authorised[] entry — the register as published
Each entry is the register content relayed as published, never reformulated —
the same ClaimView shape as /label/claims/check,
carrying only authorised claims here:
| Field | Type | Description |
|---|---|---|
code | string | Register code (e.g. POL-HC-00157) |
type | string | Base article (e.g. Art. 13.1); omitted if not published |
status | string | Verbatim register status — authorised (only authorised claims are listed here) |
nutrient | string | The nutrient / substance the claim is about |
wording | string | The exact LEGAL wording of the register — what the agent may copy, character for character |
conditions_of_use | string | Conditions of use as written; omitted if none |
efsa | object | { question, url } of the referenced EFSA opinion; omitted if none |
legislation | string | The implementing act; omitted if none |
The wording field is the register’s legal wording. An agent building a
compliant label copies this, not a paraphrase — that is the whole value of the
endpoint.
Example — a nutrient with authorised claims (billed)
GET /label/claims/permitted?nutrient=vitamin%20c — the nutrient is in the
register with authorised claims, so the verdict is found:
{
"data": {
"nutrient": "vitamin c",
"verdict": "found",
"authorised": [
{
"code": "POL-HC-00157",
"type": "Art. 13.1",
"status": "authorised",
"nutrient": "Vitamin C",
"wording": "Vitamin C contributes to normal collagen formation for the normal function of blood vessels",
"conditions_of_use": "The claim may be used only for food which is at least a source of vitamin C as referred to in the claim SOURCE OF [NAME OF VITAMIN/S] and/or [NAME OF MINERAL/S] as listed in the Annex to Regulation (EC) No 1924/2006.",
"efsa": {
"question": "Q-2008-1226",
"url": "https://www.efsa.europa.eu/en/efsajournal/pub/Q-2008-1226"
},
"legislation": "Commission Regulation (EU) No 432/2012"
}
],
"non_authorised_count": 0
},
"provenance": {
"source": "EU Register on nutrition and health claims",
"fetched_at": "2026-07-24T09:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-23T00:00:00Z" }
}
}
The wording is the legal text as published; the agent copies it verbatim.
non_authorised_count is 0 here — every listed claim is authorised.
Example — a known nutrient with no authorised claim (billed)
A nutrient the register carries but whose claims were all rejected returns a
200, billed, with an empty authorised list explained by
non_authorised_count:
{
"data": {
"nutrient": "isomaltulose",
"verdict": "none_authorised",
"authorised": [],
"non_authorised_count": 1
},
"provenance": {
"source": "EU Register on nutrition and health claims",
"fetched_at": "2026-07-24T09:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-23T00:00:00Z" }
}
}
The empty list is not a silence: non_authorised_count: 1 says the nutrient is
in the register and its claim was refused — an honest none_authorised, not an
unknown_nutrient. To read the full rejection reason and EFSA opinion for that
claim, check its wording with /label/claims/check
(the isomaltulose sentinel).
Example — a nutrient absent from the register (billed)
A nutrient the register does not carry returns a 200, billed, with an empty list and a dated limit note in plain sight, never a 404:
{
"data": {
"nutrient": "ginkgo biloba",
"verdict": "unknown_nutrient",
"authorised": [],
"non_authorised_count": 0,
"note": "Absence from the register is not an authorisation and not a prohibition. In particular, botanical claims 'on hold' under Article 13(1) are not in the register and may still be usable during the transitional regime."
},
"provenance": {
"source": "EU Register on nutrition and health claims",
"fetched_at": "2026-07-24T09:00:00Z",
"freshness": { "kind": "snapshot", "as_of": "2026-07-23T00:00:00Z" }
}
}
The honest limit: absent from the register ≠ prohibited. non_authorised_count
is 0 (not a single claim, authorised or rejected, is on file), and the note is
careful never to read “not in the register” as “forbidden” — the botanicals ‘on
hold’ regime (Article 13(1)) is the canonical case of a usable claim that is not
in the register.
Complementary to the claim check
The two claims endpoints read the same dated register from opposite directions — use them together to build and then verify a label:
| Endpoint | Input | Answer |
|---|---|---|
GET /label/claims/permitted (this page) | a nutrient | the authorised claims you may write (legal wording) |
POST /label/claims/check | a proposed wording | whether that text is authorised / non-authorised / absent |
An agent typically lists the authorised claims for its nutrient here, copies
the legal wording, then checks its final label text with /label/claims/check
before shipping.
Freshness
Answers are served from a snapshot of the register, dated by the extraction
served (freshness.as_of), not by the ingestion date. The register is refreshed
off-band; if a refresh is missed, the service keeps serving the last good snapshot
— honestly dated by as_of — and never turns staleness into a 5xx. A dataset
not yet ingested at all is a 503 DATA_UNAVAILABLE (there is no last good
snapshot to serve).
Errors
Only requests the service cannot answer leave the 200 range — none of them are billed.
| Status | code | Case |
|---|---|---|
| 400 | MISSING_PARAMETER | nutrient absent, empty, or empty after trim |
| 503 | DATA_UNAVAILABLE | The register dataset has not been ingested yet — there is no last good snapshot to serve |
| 404 | NOT_FOUND | Unknown route |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "nutrient is required", "code": "MISSING_PARAMETER" }
A 503 for a dataset that is not yet ingested is distinct from staleness: a
missed refresh serves the last snapshot, flagged by freshness.as_of, and is
never a 5xx.
Attribution
Register data is the European Commission — EU Register of nutrition and health claims made on foods, reused with attribution under Commission Decision 2011/833/EU. EFSA opinions are referenced by their question number and URL.
See also
POST /label/claims/check— the counterpart: check a proposed label wording against the same register.GET /label/chem/classify— the harmonised CLP classification of a substance (Annex VI), versioned by ATP.GET /label/chem/svhc— the SVHC Candidate List status of a substance (REACH Article 59(10)).- For agents — discovery surfaces, the live
/catalogand how settlement works.