Validate — system conformance and content coverage
The validate endpoint checks any passport address, on DPP Agent or anywhere else: how the passport is served (identifier, content negotiation, linkset, passport header, back-up declaration) and how much of the ESPR fields its data covers. It is the engine behind dppagent.com/validate.
It is CORS-open and works without a key. With an API key the answer includes field-level detail and the anonymous limits do not apply.
Check a passport address
GET /api/v1/validate?url=https://example.dpp.dppagent.com/01/09506000134352
or
POST /api/v1/validate
Content-Type: application/json
{ "url": "https://example.dpp.dppagent.com/01/09506000134352" }
The endpoint fetches the address as JSON, as HTML and as a linkset,
and scores what it gets back. Add include_document=1 (GET) or
"include_document": true (POST) to receive the passport data it read.
Response
{
"url": "https://example.dpp.dppagent.com/01/09506000134352",
"fetchedAt": "2026-10-12T15:59:50.546Z",
"dataAvailable": true,
"grade": "B",
"overallScore": 86,
"gradeBasis": "content",
"verdict": "…",
"publishable": true,
"systemConformance": {
"score": 94,
"assessed": 11,
"unverifiable": 0,
"reachedPublisher": true,
"checks": [
{ "standard": "EN 18219", "requirement": "…", "status": "pass", "pass": true, "detail": "…" }
]
},
"passportHeader": { "score": 100, "presentCount": 7, "totalCount": 7 },
"contentCoverage": {
"score": 85,
"presentCount": 11,
"totalCount": 13,
"schema": { "id": "espr-base", "name": "ESPR Base" },
"notApplicableCount": 2,
"topMissing": [ { "name": "…", "citation": "…" } ]
},
"cirpass2": {
"title": "CIRPASS-2 data model",
"note": "For information. CIRPASS-2 is an EU-funded project, and its data model is not a legal requirement. …",
"docsUrl": "https://cirpass-2.github.io/ontologies-docs/ontologies.html",
"rows": [
{
"term": "product_name",
"name": "Product Name",
"status": "matches",
"eudpp": [ { "name": "productName", "uri": "https://w3id.org/eudpp#productName", "match": "exact" } ],
"matchNote": null
}
]
},
"categoryDetected": "textile",
"product": { "name": "…", "brand": "…", "gtin": "…", "category": "textile" },
"narrative": "…",
"detail": "public"
}
systemConformance— each check names the standard and clause it tests and whether it passed. Checks that cannot be judged from outside are counted inunverifiablerather than failed.reachedPublisher: falsemeans the address did not answer; no grade is given then.passportHeader— how many of the EN 18223 header attributes the passport publishes.contentCoverage— how many of the ESPR fields for the detected category are filled.basissays whether a delegated act for the category has been adopted.cirpass2— for information only. CIRPASS-2 is an EU-funded project, and its data model is not a legal requirement. Each row is one of our fields that has a term in the CIRPASS-2 ontologies (eudpp), withmatchexact(same meaning and unit) orclose(near, andmatchNotesays how it differs).statusismatcheswhen the passport fills the field,missingwhen it does not, andnot_verifiablewhen the passport data could not be read.termis the term in our vocabulary, /vocab/dpp/v1. Only fields that exist for the detected category are listed. This section never changesgrade,overallScore,contentCoverageor any other count, and anonymous and keyed callers get the same rows.
Anonymous callers ("detail": "public") get the scores, every
system check and up to six missing fields by name. With an API key
("detail": "full") the present and missing fields are listed with
their keys and citations.
Score a document before publishing
POST /api/v1/validate?schema=auto
Content-Type: application/json
{
"document": {
"productIdentification": { "gtin": "09506000134352", "category": "electronics" },
"productData": {
"espr.pid.product_name": "Kompas Portable Speaker",
"espr.mfr.name": "Example Audio A/S"
}
},
"categoryHint": "electronics"
}
document is a passport in the JSON shape the resolver serves.
schema (query or body) is auto (default: chosen from the
category), espr-base, cirpass, agec, battery-regulation,
toy-safety, food-information or all. Nothing is fetched or
logged; the response is the coverage report with "detail": "report",
and carries the same cirpass2 section beside it.
Limits
| Caller | Limit |
|---|---|
| Anonymous | 20 requests a minute and 60 address checks an hour per IP address |
| With an API key | the anonymous limits do not apply |
An anonymous caller over the hourly limit gets 429 with
Retry-After: 3600. Responses to anonymous address checks carry
X-RateLimit-Limit and X-RateLimit-Remaining for the hourly count.
From a script or an agent
const r = await fetch('https://dppagent.com/api/v1/validate?url=' + encodeURIComponent(passportUrl));
const report = await r.json();