GET /label/chem/svhc
Resolves a substance identifier into its SVHC Candidate List status under REACH Article 59(10) (ECHA): is the substance a Substance of Very High Concern on the Candidate List — and if so, the Article 57 reasons for its inclusion, the inclusion date, the ECHA decision reference, and the generic supply-chain obligations its listing triggers (REACH Article 33 information, SCIP notification). The answer is served from a local store built from the official Candidate List, 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 placing a
product or a formulation on the EU market — “is this substance on the SVHC
Candidate List?”, “what are the reasons for inclusion, and what supply-chain
duties does that trigger?”. This is the differentiator: the Candidate List grows
~2× a year (253 entries and counting, well past model cutoffs) with no
x402 offer on this status, so an LLM answers from a stale or invented list
where this endpoint answers from the last published list, honestly dated.
The angle is pre-market screening — the natural sequel to
/label/chem/classify. Lookup is by CAS, EC
or exact name only — there is no fuzzy search in v1. See the live
/catalog for the authoritative endpoint listing and price.
This endpoint returns the Candidate List entry exactly as published, and the obligations exactly as written. It is never legal advice, nor a compliance assessment of your article — the service relays the obligation an inclusion triggers, it does not decide whether that obligation applies to your specific article (the 0.1 % w/w concentration threshold, importer status, and so on). That line is the boundary of the service.
x402 golden rule: the agent pays for the answer to its question. A
well-formed key the Candidate List does not carry is a successful answer too →
200 billed with verdict: "not_listed" — the absence is the information,
never a 404. Only requests the service cannot answer — no key, several keys, a
bad checksum, an empty name — leave the 200 range and are not billed.
Parameters
Exactly one of the three keys, as a query parameter. Zero or several keys, a
bad checksum or an empty name is a 400 INVALID_INPUT. There is no at
parameter: the Candidate List is not versioned by ATP, so the service serves
the last published list and dates the verdict through freshness.as_of.
| Parameter | Type | Required | Description |
|---|---|---|---|
cas | string | one of the three | CAS number — check-digit validated (weighted-sum mod 10, the same rigour as the IBAN mod-97) |
ec | string | one of the three | EC number (EINECS/ELINCS) — check-digit validated |
name | string | one of the three | Exact Candidate List name (case and spacing folded, no fuzzy match) — the only key that resolves a UVCB with no CAS/EC |
GET /label/chem/svhc?cas=84-74-2
cas and ec are validated by their check digit before any lookup — a wrong
check digit is a 400 INVALID_INPUT with the rejected literal in the message,
not a billed miss. An exact name is never disambiguated silently: an ambiguous
name returns all of its Candidate List entries. Some SVHC are UVCB
substances (of Unknown or Variable composition) with no CAS or EC number — the
exact name is the only way to reach them.
200 response — UnifiedResponse
{
"data": { "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" }
}
}
freshness.as_of is dated by the publication of the Candidate List served,
not by the ingestion date — the dated list is the point of the answer.
Fields of data
| Field | Type | Description |
|---|---|---|
verdict | string | listed (≥ 1 Candidate List entry) or not_listed — both are billed 200s |
entries | object[] | The Candidate List entries as published (see below); empty on not_listed |
note | string | not_listed only — the dated contractual note (see below) |
A field the source does not carry is omitted from the JSON, never served
null — the one exception being included_at inside an entry, a full member of
the published entry (null when the inclusion date is not recorded).
An entries[] entry — the Candidate List as published
| Field | Type | Description |
|---|---|---|
name | string | Substance name as published |
identifiers | object[] | { ec } or { cas } — relayed individually, never paired (the list does not match EC to CAS); empty [] for a UVCB with no CAS/EC |
reasons | string[] | Article 57 reasons for inclusion, as published — cumulative (an entry can carry several) |
included_at | string | Inclusion date (YYYY-MM-DD); null when not recorded |
decision | string | ECHA decision reference; omitted when none is published |
obligations | object | { article_33, scip } — the generic obligations the listing triggers (see below) |
Identifiers are relayed exactly as published: the Candidate List lists EC and
CAS numbers without a 1:1 correspondence, so each identifier is a separate
{ ec } or { cas } object, never paired at random. The reasons,
included_at and decision are the regulatory publication, not a reformulation.
obligations — generic regulatory facts, not advice
Every listed entry carries the two supply-chain obligations that inclusion on the Candidate List triggers, served as fixed, sourced text:
| Field | Description |
|---|---|
article_33 | REACH Article 33 — information in the supply chain for an article containing the substance above 0.1 % w/w |
scip | SCIP notification — Waste Framework Directive 2008/98/EC, Article 9(1)(i) |
These are the same constants on every listed entry. The service relays the obligation an inclusion triggers; it does not compute whether that obligation applies to your article — it does not know your concentration, your importer status, or your product. Treat them as generic regulatory facts, not a compliance verdict on your case.
Example — a listed substance, resolved by CAS
GET /label/chem/svhc?cas=84-74-2 — an entry that carries several identifiers,
cumulative Article 57 reasons and the full obligations:
{
"data": {
"verdict": "listed",
"entries": [
{
"name": "example multi-identifier svhc",
"identifiers": [
{ "ec": "201-557-4" },
{ "cas": "117-81-7" },
{ "cas": "84-74-2" }
],
"reasons": [
"Carcinogenic (Article 57a)",
"Toxic for reproduction (Article 57c)",
"Endocrine disrupting properties (Article 57(f) - human health)"
],
"included_at": "2021-01-19",
"decision": "ED/01/2021",
"obligations": {
"article_33": "REACH Article 33: a supplier of an article containing this substance in a concentration above 0.1 % weight by weight (w/w) must provide the recipient with sufficient information, available to the supplier, to allow safe use of the article, including as a minimum the name of the substance; on request, the same information must be provided to a consumer within 45 days, free of charge.",
"scip": "SCIP notification (Waste Framework Directive 2008/98/EC, Article 9(1)(i)): suppliers of articles containing this substance in a concentration above 0.1 % weight by weight (w/w) placed on the EU market must submit information on those articles to the ECHA SCIP database."
}
}
]
},
"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" }
}
}
Example — a UVCB reached by its exact name
Some SVHC have no CAS or EC number (UVCB substances). They are reachable
only by their exact Candidate List name, and the entry carries an empty
identifiers array:
"entries": [
{
"name": "example uvcb reaction mass",
"identifiers": [],
"reasons": ["Toxic for reproduction (Article 57c)"],
"included_at": "2018-06-27",
"obligations": { "article_33": "...", "scip": "..." }
}
]
The decision field is absent here because the source publishes none — an
omitted field is never served as null.
Example — a well-formed key that is not listed (billed)
GET /label/chem/svhc?cas=7732-18-5 (water) — a valid CAS the Candidate List
does not carry — returns a 200, billed, with a dated note in plain sight,
never a 404:
{
"data": {
"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."
},
"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" }
}
}
The agent asked “is this substance on the Candidate List?”, and “not on the list
published on <as_of>” is the answer — dated, and careful never to say “never
listed” or “of no concern”. The Candidate List grows over time, so the note is
scoped to the served publication date. Contrast this with the 400/503
below, which are not billed.
Freshness
Answers are served from a snapshot of the Candidate List, dated by the
publication of the list served (freshness.as_of), not by the ingestion
date. The list is refreshed off-band from the official ECHA publication (a manual
deposit, once or twice a year); 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 | INVALID_INPUT | Zero or several keys; a bad CAS/EC check digit; an empty name |
| 503 | DATA_UNAVAILABLE | The Candidate List 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": "invalid CAS check digit: '84-74-3'", "code": "INVALID_INPUT" }
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
Candidate List data is the ECHA Candidate List of Substances of Very High Concern for authorisation, a legal publication under REACH Article 59(10) (ECHA legal notice — commercial reuse permitted).
See also
POST /label/chem/svhc/batch— screen a whole BOM or formulation (up to 200 substances) in one settlement.GET /label/chem/classify— the harmonised CLP classification of a substance (Annex VI), versioned by ATP.- For agents — discovery surfaces, the live
/catalogand how settlement works.