GET /label/cosmetic/ingredient
Resolves a cosmetic ingredient into its regulatory status under Regulation (EC) No 1223/2009: prohibited (Annex II), restricted (Annex III), or authorised as a colorant (IV), preservative (V) or UV filter (VI) — with every published entry, its maximum concentration, its conditions of use and the mandatory label wording served word for word. The answer comes from a local store built from the consolidated text on EUR-Lex, so a lookup resolves in milliseconds with no network call and no secret at request time.
Use it when an agent needs a grounded, dated, sourced verdict before shipping an
EU cosmetic label or clearing a formulation — “may this ingredient be used at
all?”, “at what concentration, in which product type, and what must the label
say?”. The answer is versioned by amending regulation: the consolidated
version in force at the queried date, plus the upcoming change when a published
but not-yet-applicable one modifies the entry. See the live
/catalog for the authoritative endpoint listing and price.
Only the Annexes are authoritative — CosIng is informative
This is the nuance that decides whether an answer is usable. CosIng, the European Commission’s cosmetic-ingredient database, is a legally non-binding information tool: only the Annexes to the Regulation are authoritative. This service therefore keeps the two apart and says which one spoke:
substance.resolved_from: "annexes"— the identity comes from the authoritative source itself;substance.resolved_from: "cosing_index"— CosIng recognised the substance, which is what turns an unknown identifier into an honestnot_listedverdict instead of a 404;source_divergence: truewith adivergences[]array — a recorded gap between CosIng and an Annex applies to this substance. The Annex always prevails, and the gap is reported, never silently resolved.
The second nuance is stated in the response itself, in limits: absence from
Annexes II to VI is not an authorisation. It means no specific EU-wide
prohibition or restriction entry applies — the safety assessment of the
ingredient and of the finished product remains the responsibility of the
responsible person placing it on the market.
x402 golden rule: the agent pays for the answer to its question. A resolved
identity that the Annexes do not carry is a successful answer too → 200
billed with verdict: "not_listed" — the absence is the information. Only
requests the service cannot answer — zero or several keys, a bad checksum, an
identity unknown to both the Annexes and CosIng, a date before the oldest
ingested version — leave the 200 range and are not billed.
Parameters
Exactly one of the four keys, as a query parameter.
| Parameter | Type | Required | Description |
|---|---|---|---|
cas | string | one of the four | CAS number — check-digit validated before any lookup |
ec | string | one of the four | EC number — check-digit validated |
inci | string | one of the four | Exact INCI name (case and spacing folded, no fuzzy match) |
name | string | one of the four | Exact chemical name |
date | string | no | YYYY-MM-DD — serves the consolidated version in force at that date (defaults to today) |
product_type | string | no | Keeps only the conditions whose published scope covers that product type, plus the unbounded ones |
GET /label/cosmetic/ingredient?inci=SALICYLIC%20ACID
GET /label/cosmetic/ingredient?cas=123-31-9&date=2026-06-01
A date earlier than the oldest ingested consolidated version is a 400 naming
the bound: extrapolating a verdict backwards would be factually wrong.
product_type, when provided, must not be empty.
200 response — UnifiedResponse
{
"data": {
"verdict": "restricted",
"substance": { "...": "..." },
"entries": [ "..." ],
"version": { "...": "..." },
"source_divergence": false,
"note": "...",
"limits": ["..."]
},
"provenance": {
"source": "Regulation (EC) No 1223/2009 on cosmetic products — consolidated text (EUR-Lex), Annexes II to VI",
"fetched_at": "2026-07-30T18:27:53Z",
"freshness": { "kind": "snapshot", "as_of": "2026-05-01T00:00:00Z" }
}
}
Fields of data
| Field | Type | Description |
|---|---|---|
verdict | string | Closed enumeration: prohibited (II), restricted (III), authorised_colorant (IV), authorised_preservative (V), authorised_uv_filter (VI), not_listed |
substance | object | The resolved identity (inci_name, chemical_name, cas_numbers, ec_numbers) with resolved_from |
entries | object[] | One entry per Annex covering the substance, as published (see below); empty on not_listed |
version | object | The consolidated version served: consolidated_ref, applicable_from, applicable_to (null when open) |
source_divergence | bool | true when a recorded CosIng/Annex divergence applies; divergences[] then details it |
note | string | Why the top-level verdict reads as it does — multi-Annex coverage, or the dated not_listed statement |
limits | string[] | The four fixed caveats carried by every response |
A substance can fall under several Annexes. The top-level verdict is then
the most restrictive one, but every entry keeps its own status and all of
them apply cumulatively — note says so explicitly. Reading only the top-level
verdict on such a substance loses half the obligations.
An entries[] entry — the Annex as published
| Field | Type | Description |
|---|---|---|
annex | string | II to VI |
entry_number | string | Reference number in that Annex |
status | string | The status of this entry (same enumeration as verdict, minus not_listed) |
conditions | object[] | product_type_or_body_part, max_concentration, other_conditions, required_label_wording — served word for word |
conditions_omitted_by_product_type | int | How many conditions the product_type filter removed — nothing is hidden |
amending_act | string | The amending regulation, with amending_celex and amending_oj_date |
applicable_from | string | Applicability date of the version served |
upcoming | object | { change, applicable_from, consolidated_ref } when a published but not-yet-applicable version adds, modifies or removes the entry |
Maximum concentrations, conditions of use and label wording are relayed exactly
as published — no unit conversion, no rounding, no reformulation, no
translation. "3,0 %" keeps its comma; a multi-part condition keeps its (a)
(b) (c) structure and its line breaks, because those parts are what bind the
concentration to the product type.
Example — a substance covered by two Annexes
GET /label/cosmetic/ingredient?inci=SALICYLIC%20ACID — restricted under
Annex III and authorised as a preservative under Annex V. The verdict is the
most restrictive; both entries apply.
{
"data": {
"verdict": "restricted",
"substance": {
"inci_name": "Salicylic acid",
"chemical_name": "Benzoic acid, 2-hydroxy- (9)",
"cas_numbers": ["69-72-7"],
"ec_numbers": ["200-712-3"],
"resolved_from": "annexes"
},
"entries": [
{
"annex": "III",
"entry_number": "98",
"status": "restricted",
"conditions": [
{
"product_type_or_body_part": "(a) Rinse-off hair products\n(b) Other products except body lotion, eye shadow, mascara, eyeliner, lipstick, roll-on deodorant\n(c) Body lotion, eye shadow, mascara, eyeliner, lipstick, roll-on deodorant",
"max_concentration": "(a) 3,0 %\n(b) 2,0 %\n(c) 0,5 %",
"other_conditions": "(a) (b) (c)\nNot to be used in preparations for children under 3 years of age. …",
"required_label_wording": "(a) (b) (c)\nNot to be used for children under 3 years of age (10)"
}
],
"amending_act": "COMMISSION REGULATION (EU) 2021/850 of 26 May 2021",
"amending_celex": "32021R0850",
"amending_oj_date": "2021-05-28",
"applicable_from": "2026-05-01"
},
{
"annex": "V",
"entry_number": "3",
"status": "authorised_preservative",
"conditions": [
{
"max_concentration": "0,5 % (acid)",
"other_conditions": "Not to be used in products for children under 3 years of age, except for shampoos.",
"required_label_wording": "Not to be used for children under 3 years of age (2)"
}
],
"amending_act": "COMMISSION REGULATION (EU) 2019/1966 of 27 November 2019",
"amending_celex": "32019R1966",
"amending_oj_date": "2019-11-28",
"applicable_from": "2026-05-01"
}
],
"version": {
"consolidated_ref": "02009R1223 — EN — 01.05.2026",
"applicable_from": "2026-05-01",
"applicable_to": null
},
"source_divergence": false,
"note": "This substance is covered by 2 entries (III/98, V/3). The top-level verdict reflects the most restrictive one; each entry carries its own status and conditions, which all apply cumulatively.",
"limits": [
"Absence from Annexes II to VI of Regulation (EC) No 1223/2009 is not an authorisation: it means no specific EU-wide prohibition or restriction entry applies. The safety assessment of the ingredient and of the finished product remains the responsibility of the responsible person placing it on the market.",
"This is not a safety assessment: no concentration in a finished formulation is computed, and no conclusion is drawn on a specific product.",
"Maximum concentrations, conditions of use and label wording are served exactly as published, without unit conversion, rounding, reformulation or translation ('quantum satis' included).",
"CosIng is a legally non-binding information tool of the European Commission: only the Annexes to the Regulation are authoritative. Divergences are reported, never silently resolved."
]
},
"provenance": {
"source": "Regulation (EC) No 1223/2009 on cosmetic products — consolidated text (EUR-Lex), Annexes II to VI",
"fetched_at": "2026-07-30T18:27:53Z",
"freshness": { "kind": "snapshot", "as_of": "2026-05-01T00:00:00Z" }
}
}
Adding &product_type=Rinse-off%20hair%20products keeps the Annex III entry
whole (its conditions are published as one multi-part block) and trims the Annex V
entry from five conditions to four, reporting
"conditions_omitted_by_product_type": 1. The filter is a reading aid that
counts what it removed — it never lets an agent believe it saw everything.
Example — prohibited, with a narrow authorised exception
GET /label/cosmetic/ingredient?cas=123-31-9 (hydroquinone) is prohibited under
Annex II and carries an Annex III entry for artificial nail systems only:
{
"verdict": "prohibited",
"entries": [
{
"annex": "II",
"entry_number": "1339",
"status": "prohibited",
"conditions": [],
"amending_act": "COMMISSION REGULATION (EU) No 344/2013 of 4 April 2013"
},
{
"annex": "III",
"entry_number": "14",
"status": "restricted",
"conditions": [
{
"product_type_or_body_part": "Artificial nail systems",
"max_concentration": "0,02 % (after mixing for use)",
"other_conditions": "Professional use only",
"required_label_wording": "— For professional use only\n— Avoid skin contact\n— Read directions for use carefully"
}
]
}
]
}
The three label lines are the regulatory text, served verbatim, dashes included — that is what goes on the label.
Example — not listed is a billed 200, not an authorisation
GET /label/cosmetic/ingredient?cas=56-81-5 (glycerin): CosIng recognises the
substance, the Annexes do not carry it. The verdict is dated and says what it
does not mean:
{
"verdict": "not_listed",
"substance": {
"inci_name": "GLYCERIN",
"cas_numbers": ["56-81-5"],
"ec_numbers": ["200-289-5"],
"resolved_from": "cosing_index"
},
"entries": [],
"note": "Not listed in Annexes II to VI of Regulation (EC) No 1223/2009 as in force on 2026-07-30 (consolidated version 02009R1223 — EN — 01.05.2026 of the Regulation). This verdict is dated and is not an authorisation: it means no specific prohibition or restriction entry applies to this substance at that date."
}
resolved_from: "cosing_index" is what makes this verdict honest rather than a
guess: the informative source vouched for the identity, the authoritative
Annexes answered on the status.
What this endpoint refuses to do
The four limits are content, not a footer:
- absence from the Annexes is not an authorisation — the safety assessment stays with the responsible person;
- this is not a safety assessment: no concentration in a finished formulation is computed and no conclusion is drawn on a specific product;
- values are served exactly as published, without conversion or reformulation;
- CosIng is informative only; only the Annexes are authoritative, and divergences are reported.
There is also no fuzzy search: an approximate INCI name is a miss, not a guess.
Freshness
Answers are served from a snapshot of the consolidated Regulation, dated by
the version in force at the queried date (freshness.as_of), not by the
ingestion date. A missed refresh keeps serving the last good snapshot, honestly
dated, and never becomes a 5xx.
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 bad CAS/EC check digit; an empty product_type; a date before the oldest ingested version |
| 404 | NOT_FOUND | The identity is unknown to both the Annexes and the CosIng index |
| 503 | DATA_UNAVAILABLE | The cosmetics dataset has not been ingested yet — there is no last good snapshot to serve |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "parameters 'cas', 'ec', 'inci' and 'name' are mutually exclusive", "code": "INVALID_INPUT" }
{ "error": "invalid CAS check digit: '123-31-8'", "code": "INVALID_INPUT" }
{ "error": "cosmetic ingredient identity not resolved: unknown to Annexes II to VI of Regulation (EC) No 1223/2009 and to the CosIng index", "code": "NOT_FOUND" }
An unresolved identity is a 404 (nothing to answer about); a resolved
identity absent from the Annexes is a billed 200 not_listed. The two are
never confused.
Attribution
Verdicts are derived from Regulation (EC) No 1223/2009 on cosmetic products, consolidated text published on EUR-Lex (European Union), with the CosIng index of the European Commission used for identity resolution only.
See also
POST /label/cosmetic/ingredient/batch— screen a whole INCI list (up to 200 ingredients) in one settlement.GET /label/food/additive— the same discipline for food additives, where the verdict needs a food category.GET /label/chem/classify— harmonised CLP classification of a substance, versioned by ATP.- For agents — discovery surfaces, the live
/catalogand how settlement works.