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"
}
FieldTypeRequiredDescription
itemsobject[]yes1 to 200 items; each carries exactly one of cas / ec / inci / name, same rules as the single call
datestringnoYYYY-MM-DDcommon to the whole batch; defaults to today
product_typestringnoCommon 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" }
  }
}
FieldTypeDescription
countintNumber of submitted items — always equal to results.length
resultsarrayOne entry per submitted item, at its position: the same shape as the single call, minus version and limits (factored at batch level)
summaryobjectCount per verdict plus error
versionobjectThe consolidated version the whole batch was evaluated against
limitsstring[]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 unit 404).

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.

StatuscodeCase
400INVALID_INPUTMissing or malformed body; empty items; more than 200 items; empty product_type; a date before the oldest ingested version
503DATA_UNAVAILABLEThe cosmetics dataset has not been ingested yet
500INTERNALInternal 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