POST /label/cosmetic/ingredient/batch
Screens a whole INCI list in a single call: up to 200 ingredients, each
by cas, ec, inci or exact name, all evaluated at one date, settled
with one x402 payment for the batch. Each item runs through the same engine as
GET /label/cosmetic/ingredient — the
verdict under Annexes II to VI of Regulation (EC) No 1223/2009, with the
published entries, maximum concentrations, conditions of use and mandatory
label wording served word for word. The lookup is purely local → response in
milliseconds, no network call and no secret.
One settlement for N ingredients is the formulation screening path: an agent
checks a full ingredient list before shipping an EU label. The batch is settled as
one x402 payment for the whole call and priced per unit by the gateway
(the first ingredient is roughly the price of the single call, then each
additional one adds to the total) — see the live
/catalog for the authoritative price.
This is not a formulation compliance verdict. No concentration in the finished product is computed, and nothing is concluded about the product as a whole. The input order is preserved but never interpreted: an INCI list is conventionally written in descending concentration order, and that convention is deliberately not exploited — inferring concentrations from position would be inventing data. There is no de-duplication either.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer → 200, even when items are
not_listed and even when a single item is malformed or unresolved (it is
returned with { error, code } at its position, without failing the batch).
The 4xx range is reserved for requests the service cannot answer: a missing or
malformed body, an empty list, a batch over the cap, or a date before the
oldest ingested version.
Request
POST with a JSON body. Set Content-Type: application/json.
POST /label/cosmetic/ingredient/batch
Content-Type: application/json
{
"items": [{ "inci": "Octocrylene" }, { "cas": "56-81-5" }],
"date": "2026-06-01",
"product_type": "Rinse-off hair products"
}
| Field | Type | Required | Description |
|---|---|---|---|
items | object[] | yes | 1 to 200 items; each carries exactly one of cas / ec / inci / name, same rules as the single call |
date | string | no | YYYY-MM-DD — common to the whole batch; defaults to today |
product_type | string | no | Common to the whole batch: keeps the conditions published for that product type plus the unbounded ones |
A single date for the whole batch is deliberate: a formulation is assessed at
one regulatory version. The cap is checked before any lookup, so an empty
list or one over 200 is rejected without being billed.
200 response — UnifiedResponse
{
"data": {
"count": 4,
"results": [ "..." ],
"summary": { "...": "..." },
"version": { "...": "..." },
"limits": ["..."]
},
"provenance": {
"source": "Regulation (EC) No 1223/2009 on cosmetic products — consolidated text (EUR-Lex), Annexes II to VI",
"fetched_at": "2026-07-30T18:28:36Z",
"freshness": { "kind": "snapshot", "as_of": "2026-05-01T00:00:00Z" }
}
}
| Field | Type | Description |
|---|---|---|
count | int | Number of submitted items — always equal to results.length |
results | array | One entry per submitted item, at its position: the same shape as the single call, minus version and limits (factored at batch level) |
summary | object | Count per verdict plus error |
version | object | The consolidated version the whole batch was evaluated against |
limits | string[] | The four fixed caveats, served once at batch level (they are identical for every item) |
summary is always fully serialised, zeros included: "prohibited": 0 is
information — no prohibited substance in this formulation — and an agent that
had to infer it from an absent key would be inferring from silence.
A malformed or unresolved item stays at its position
Two per-item failures are possible, and neither fails the batch:
{ "error": "...", "code": "INVALID_INPUT" }— zero or several keys, a bad CAS/EC check digit;{ "error": "...", "code": "NOT_FOUND" }— the identity is unknown to both the Annexes and the CosIng index (the counterpart of the unit404).
They keep their index, so an agent can map every result back to the ingredient it sent.
Example — four ingredients, two verdicts and two per-item errors
{
"data": {
"count": 4,
"results": [
{
"verdict": "authorised_uv_filter",
"substance": {
"inci_name": "Octocrylene",
"chemical_name": "2-Cyano-3,3-diphenyl acrylic acid, 2- ethylhexyl ester/Octocrylene (6) (7)",
"cas_numbers": ["6197-30-4"],
"ec_numbers": ["228-250-8"],
"resolved_from": "annexes"
},
"entries": [
{
"annex": "VI",
"entry_number": "10",
"status": "authorised_uv_filter",
"conditions": [
{
"product_type_or_body_part": "a) Propellant spray products\nb) Other products",
"max_concentration": "a) 9 %\nb) 10 %"
}
],
"amending_act": "COMMISSION REGULATION (EU) 2022/1176 of 7 July 2022",
"amending_celex": "32022R1176",
"amending_oj_date": "2022-07-08",
"applicable_from": "2026-05-01"
}
],
"source_divergence": false
},
{
"verdict": "not_listed",
"substance": {
"inci_name": "GLYCERIN",
"cas_numbers": ["56-81-5"],
"ec_numbers": ["200-289-5"],
"resolved_from": "cosing_index"
},
"entries": [],
"source_divergence": false,
"note": "Not listed in Annexes II to VI of Regulation (EC) No 1223/2009 as in force on 2026-07-30 … This verdict is dated and is not an authorisation …"
},
{
"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"
},
{
"error": "invalid CAS check digit: '123-31-8'",
"code": "INVALID_INPUT"
}
],
"summary": {
"prohibited": 0,
"restricted": 0,
"authorised_colorant": 0,
"authorised_preservative": 0,
"authorised_uv_filter": 1,
"not_listed": 1,
"error": 2
},
"version": {
"consolidated_ref": "02009R1223 — EN — 01.05.2026",
"applicable_from": "2026-05-01",
"applicable_to": null
},
"limits": [
"Absence from Annexes II to VI of Regulation (EC) No 1223/2009 is not an authorisation: …",
"This is not a safety assessment: …",
"Maximum concentrations, conditions of use and label wording are served exactly as published, …",
"CosIng is a legally non-binding information tool of the European Commission: only the Annexes to the Regulation are authoritative. …"
]
},
"provenance": {
"source": "Regulation (EC) No 1223/2009 on cosmetic products — consolidated text (EUR-Lex), Annexes II to VI",
"fetched_at": "2026-07-30T18:28:36Z",
"freshness": { "kind": "snapshot", "as_of": "2026-05-01T00:00:00Z" }
}
}
Two ingredients answered, two rejected at their positions, and a summary whose
zeros are as informative as its ones. The whole call is a single settlement.
What this endpoint refuses to do
Everything the single call refuses, plus the batch-specific line: the order of
the list is not a concentration signal. The four limits come back once per
batch — absence is not an authorisation, this is not a safety assessment, values
are served exactly as published, and CosIng is informative only while the Annexes
are authoritative.
Errors
Only requests the service cannot answer leave the 200 range — none of them are
billed. Per-item failures are not in this table: they live inside
results.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_INPUT | Missing or malformed body; empty items; more than 200 items; empty product_type; a date before the oldest ingested version |
| 503 | DATA_UNAVAILABLE | The cosmetics dataset has not been ingested yet |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "batch must contain at least one item", "code": "INVALID_INPUT" }
{ "error": "batch size exceeds the maximum of 200 items", "code": "INVALID_INPUT" }
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
GET /label/cosmetic/ingredient— the single-ingredient call, with the full field reference and the CosIng/Annex hierarchy.POST /label/chem/classify/batch— the same batch discipline for harmonised CLP classification.- For agents — discovery surfaces, the live
/catalogand how settlement works.