POST /label/chem/svhc/batch
Screens a list of substances in a single call: up to 200 entries, each by
cas, ec or exact name, all resolved against the last published Candidate
List, settled with one x402 payment for the whole batch. Each item runs
through the same engine as GET /label/chem/svhc —
the SVHC Candidate List status under REACH Article 59(10). The lookup is
purely local → response in milliseconds, no network call and no secret.
One settlement for N substances is the BOM / formulation screening path: an
agent checks a bill of materials for Substances of Very High Concern before
placing a product on the EU market. The batch is settled as one x402 payment
for the whole call and priced per unit by the gateway (the first substance
is roughly the price of the single call, then each additional substance adds to
the total) — see the live /catalog for the authoritative
price.
x402 golden rule: the agent pays for the answer to its question. A
well-formed batch is a successful answer → 200, even when some substances are
not_listed and even when a single item is malformed (that item is returned with
{ error, code } at its position, without failing the batch). The 4xx range
is reserved for requests the service cannot answer (missing or malformed body,
empty list, batch over the cap).
Request
POST with a JSON body. Set Content-Type: application/json. There is no
batch-level date: the Candidate List is not versioned by ATP, so every item is
resolved against the last published list.
POST /label/chem/svhc/batch
Content-Type: application/json
{
"items": [{ "cas": "84-74-2" }, { "cas": "7732-18-5" }, { "name": "example uvcb reaction mass" }]
}
| Field | Type | Required | Description |
|---|---|---|---|
items | object[] | yes | 1 to 200 items; each carries exactly one of cas / ec / name, same rules as the single call |
The cap is checked before any lookup, so an empty list or one over 200 is
rejected without being billed an answer. There is no at field — unlike
/label/chem/classify/batch, the Candidate
List is served as its last published version.
200 response — UnifiedResponse
{
"data": {
"count": 3,
"results": [
{ "verdict": "listed", "entries": [ ... ] },
{
"verdict": "not_listed",
"entries": [],
"note": "Not on the ECHA Candidate List of SVHC as published on 2026-06-27. This means the substance was not listed at that date, not that it never will be; the Candidate List grows over time. Absence from the list does not mean the substance is of no concern."
},
{ "verdict": "listed", "entries": [ ... ] }
]
},
"provenance": {
"source": "ECHA Candidate List of SVHC (REACH Art. 59(10))",
"fetched_at": "2026-07-24T07:45:50Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-27T00:00:00Z" }
}
}
count: number of results, equal toresults.lengthand to the number of items submitted.results: one verdict per item, in the same order as the input. Each well-formed element has exactly the same shape as thedataofGET /label/chem/svhc—verdict,entries[], and the datednoteonnot_listed.- A single
provenanceblock covers the whole batch: every item is resolved against the same Candidate List snapshot.
See the single-call reference for the full field
tables, the Article 57 reasons, the generic Article 33 + SCIP obligations and the
dated not_listed note.
A malformed item stays at its position
A single bad item does not fail the batch: it is returned in place as
{ error, code }, so a caller can re-pair results without tracking indices.
{
"data": {
"count": 2,
"results": [
{ "error": "invalid CAS check digit: '84-74-3'", "code": "INVALID_INPUT" },
{ "verdict": "listed", "entries": [ ... ] }
]
},
"provenance": {
"source": "ECHA Candidate List of SVHC (REACH Art. 59(10))",
"fetched_at": "2026-07-24T07:45:50Z",
"freshness": { "kind": "snapshot", "as_of": "2026-06-27T00:00:00Z" }
}
}
Errors
Only requests the service cannot answer leave the 200 range. The cap is checked before any resolution, so an oversized batch is rejected without being billed.
| Status | code | Case |
|---|---|---|
| 400 | INVALID_INPUT | Body missing or not JSON, not an object, items missing, empty, or over 200 |
| 503 | DATA_UNAVAILABLE | The Candidate List dataset has not been ingested yet — there is no snapshot to serve |
| 500 | INTERNAL | Internal error (detail logged, not exposed) |
{ "error": "batch too large: 201 items, the maximum is 200", "code": "INVALID_INPUT" }
See also
GET /label/chem/svhc— single-substance reference and full field documentation.POST /label/chem/classify/batch— screen a BOM for its harmonised CLP classification (Annex VI).- For agents — discovery surfaces, the live
/catalogand how settlement works.