API Reference
Attestd exposes REST endpoints for vulnerability checks, product catalog, CVE detail lookup, usage quota, and webhook management. All responses are JSON unless noted.
Base URL
https://api.attestd.ioAuthentication
Pass your API key in the Authorization header as a Bearer token.
Authorization: Bearer YOUR_API_KEYGet an API key from the developer portal. Use Authorization: Bearer atst_... on every request.
GET /v1/check
Returns the current risk assessment for a product version.
Query parameters
| Parameter | Description |
|---|---|
product | Required. Product slug (e.g. nginx, log4j, openssh). See Quickstart for the full list. |
version | Required. Version string in any standard format (e.g. 1.24.0, 2.14.1, 8.0p1). |
include | Optional. Pass include=cves to receive a per-CVE breakdown in the cves array. Omit for the default compact response. Any other value returns HTTP 400. |
ecosystem | Optional. npm or pypi. Pins a dual-listed package to one ecosystem. |
Example request
curl "https://api.attestd.io/v1/check?product=nginx&version=1.20.0" \
-H "Authorization: Bearer YOUR_API_KEY"Example with include=cves
Pass include=cves to receive per-CVE detail records in the cves array. See Response Fields for subfield semantics.
curl "https://api.attestd.io/v1/check?product=log4j&version=2.14.1&include=cves" \
-H "Authorization: Bearer YOUR_API_KEY"{
"product": "log4j",
"version": "2.14.1",
"supported": true,
"risk_state": "critical",
"risk_factors": [
"active_exploitation",
"remote_code_execution",
"no_authentication_required",
"internet_exposed_service",
"patch_available"
],
"actively_exploited": true,
"remote_exploitable": true,
"authentication_required": false,
"patch_available": true,
"fixed_version": "2.25.4",
"confidence": 0.5,
"cve_ids": ["CVE-2021-44228", "CVE-2021-45046", "CVE-2021-45105"],
"max_epss": 0.99999,
"cves": [
{
"cve_id": "CVE-2021-44228",
"cvss_score": 10.0,
"actively_exploited": true,
"remote_exploitable": true,
"epss_score": 0.99999,
"epss_percentile": 1.0
},
{
"cve_id": "CVE-2021-45046",
"cvss_score": 9.0,
"actively_exploited": true,
"remote_exploitable": true,
"epss_score": 0.97123,
"epss_percentile": 0.998
}
],
"last_updated": "2026-07-08T10:58:13.230818Z",
"supply_chain": null,
"typosquat": null
}Responses
200 OK(supported product){
"product": "nginx",
"version": "1.20.0",
"supported": true,
"risk_state": "critical",
"risk_factors": [
"active_exploitation",
"remote_code_execution",
"no_authentication_required",
"internet_exposed_service",
"patch_available"
],
"actively_exploited": true,
"remote_exploitable": true,
"authentication_required": false,
"patch_available": true,
"fixed_version": null,
"confidence": 0.5,
"cve_ids": [
"CVE-2021-23017",
"CVE-2021-3618",
"CVE-2022-41741",
"CVE-2022-41742",
"CVE-2023-44487",
"CVE-2025-23419",
"CVE-2026-16993",
"CVE-2026-25552",
"CVE-2026-42055",
"CVE-2026-42533",
"CVE-2026-42945",
"CVE-2026-49972",
"CVE-2026-56434",
"CVE-2026-58467",
"CVE-2026-60005",
"CVE-2026-8430"
],
"cves": null,
"max_epss": 0.99999,
"supply_chain": {
"compromised": false,
"sources": [],
"malware_type": null,
"description": null,
"advisory_url": null,
"compromised_at": null,
"removed_at": null,
"source_published_at": null,
"observed_at": null,
"ingested_at": null,
"first_served_at": null,
"provenance": null
},
"supply_chain_monitored": true,
"typosquat": null,
"last_updated": "2026-08-16T11:18:08.825063Z"
}200 OK(product not in coverage){
"supported": false,
"supply_chain_monitored": false,
"typosquat": {
"detected": true,
"kind": "hallucination",
"resembles": "@types/jscodeshift",
"likely_intended": [
"@types/jscodeshift",
"jscodeshift"
],
"confidence": 0.9,
"ecosystem": "npm"
}
}POST /v1/check/batch
Check up to 100 product versions in a single request. Each item in the batch is billed as one API call. If the batch would exceed your quota, a 429 is returned before any results are delivered and no calls are billed.
Request body
| Field | Description |
|---|---|
items | Required. Array of { product, version } objects. Minimum 1, maximum 100. |
items[].product | Product slug. Same format as the GET /v1/check product parameter. |
items[].version | Version string. Same format as the GET /v1/check version parameter. |
items[].ecosystem | Optional. npm or pypi. Pins a dual-listed package to one ecosystem. Same meaning as the GET /v1/check ecosystem query parameter. |
The optional include=cves query parameter works the same as on GET /v1/check and applies to every item in the batch.
Example request
curl -X POST "https://api.attestd.io/v1/check/batch" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"items": [
{ "product": "log4j", "version": "2.14.1" },
{ "product": "log4j", "version": "2.25.5" }
]
}'Response
Each entry in results has product, version, result, and error. When the item succeeds, result is the same shape as a GET /v1/check response and error is null. Results are returned in the same order as the input items. The HTTP status is still 200 when an item fails internally. That item has result: null and error set to internal error processing this item.
{
"results": [
{
"product": "log4j",
"version": "2.14.1",
"result": {
"product": "log4j",
"version": "2.14.1",
"supported": true,
"risk_state": "critical",
"risk_factors": [
"active_exploitation",
"remote_code_execution",
"no_authentication_required",
"internet_exposed_service",
"patch_available"
],
"actively_exploited": true,
"remote_exploitable": true,
"authentication_required": false,
"patch_available": true,
"fixed_version": "2.25.5",
"confidence": 0.5,
"cve_ids": [
"CVE-2021-44228",
"CVE-2021-44832",
"CVE-2021-45046",
"CVE-2021-45105",
"CVE-2025-68161",
"CVE-2026-34477",
"CVE-2026-34479",
"CVE-2026-34480",
"CVE-2026-34481",
"CVE-2026-49844"
],
"cves": null,
"max_epss": 0.99999,
"supply_chain": null,
"supply_chain_monitored": false,
"typosquat": null,
"last_updated": "2026-08-13T16:28:06.303403Z"
},
"error": null
},
{
"product": "log4j",
"version": "2.25.5",
"result": {
"product": "log4j",
"version": "2.25.5",
"supported": true,
"risk_state": "none",
"risk_factors": [],
"actively_exploited": false,
"remote_exploitable": false,
"authentication_required": false,
"patch_available": false,
"fixed_version": null,
"confidence": 0.9,
"cve_ids": [],
"cves": null,
"max_epss": null,
"last_updated": "2026-08-16T14:16:08.144371Z",
"supply_chain": null,
"supply_chain_monitored": false,
"typosquat": null
},
"error": null
}
],
"count": 2
}Per-item failure shape (batch HTTP status remains 200):
{
"product": "nginx",
"version": "1.20.0",
"result": null,
"error": "internal error processing this item"
}POST /v1/sbom
Upload a CycloneDX JSON BOM (specVersion 1.3 to 1.6). The body is the BOM document. Cap 2,000 flattened components, 4 MiB. One returned row is one billed call, including outside_coverage. Quota is checked before any checks run. See SBOM ingestion for mapping, container BOM billing, and SPDX status.
Mapped types: pkg:npm, pkg:pypi, and pkg:generic when the name is an Attestd product slug. Image pkg:deb / pkg:apk rows are billed and not mapped.
curl -X POST "https://api.attestd.io/v1/sbom" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/vnd.cyclonedx+json" \
--data @bom.jsonResponse leads with summary, then results in document order. Truncated. A BOM with metadata.component plus two mapped libraries is three rows and three billed calls. See SBOM ingestion for the request body.
{
"summary": {
"component_count": 3,
"mapped_count": 2,
"outside_coverage_count": 1,
"risk_state_counts": {
"critical": 0,
"high": 0,
"elevated": 0,
"low": 0,
"none": 2
},
"compromised_count": 0,
"billed_calls": 3,
"spec_version": "1.5"
},
"results": [
{
"bom_ref": null,
"purl": null,
"name": "example-app",
"version": "1.0.0",
"ecosystem": null,
"outside_coverage": true,
"outside_coverage_reason": "missing_purl",
"result": null,
"error": null
}
]
}Platform Starter accounts can register HTTPS endpoints for supply_chain.compromise events. See Supply chain webhooks for payload shape, signature verification, and delivery semantics.
GET /v1/webhooks
List active webhook subscriptions for the authenticated account. Returns 403 if the API key is not on Platform Starter.
curl "https://api.attestd.io/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY"[
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://hooks.example.com/attestd",
"events": ["supply_chain.compromise"],
"created_at": "2026-07-08T12:00:00Z"
}
]POST /v1/webhooks
Create a subscription. Body fields: url (HTTPS), secret (16 to 64 characters), events (array; currently supply_chain.compromise only). Returns 201 on success, 400 on validation failure, 403 if not Platform Starter.
curl -X POST "https://api.attestd.io/v1/webhooks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://hooks.example.com/attestd",
"secret": "your-signing-secret-min-16-chars",
"events": ["supply_chain.compromise"]
}'{
"id": "550e8400-e29b-41d4-a716-446655440000",
"url": "https://hooks.example.com/attestd",
"events": ["supply_chain.compromise"],
"created_at": "2026-07-08T12:00:00Z"
}DELETE /v1/webhooks/{id}
Soft-delete a subscription and cancel pending deliveries. Returns 204 on success, 404 if the subscription is not found or not owned by the account.
curl -X DELETE "https://api.attestd.io/v1/webhooks/550e8400-e29b-41d4-a716-446655440000" \
-H "Authorization: Bearer YOUR_API_KEY"GET /v1/coverage
Public coverage totals. No API key. Not billed. Returns current CVE product count, distinct CVE IDs, and PyPI / npm watchlist sizes. Use this for dashboards and marketing counts. Use GET /v1/products when you need the slug list.
Example request
curl "https://api.attestd.io/v1/coverage"Response
{
"cve_products": 1012,
"distinct_cves": 86010,
"supply_chain_pypi": 29252,
"supply_chain_npm": 310740,
"monitored_packages": 339992,
"generated_at": "2026-09-14T14:48:41.428126Z"
}GET /v1/products
Returns the full list of products Attestd covers: CVE-tracked software and monitored supply chain packages. Requires a valid API key. The CVE product slugs match the Products catalog.
Example request
curl "https://api.attestd.io/v1/products" \
-H "Authorization: Bearer YOUR_API_KEY"Response
Three top-level fields: cve_products (slug and display_name for each CVE product), supply_chain_packages (package name, ecosystem, and optional display_name), and total (combined count). Arrays are truncated in the example below.
{
"cve_products": [
{ "slug": "nginx", "display_name": "nginx" },
{ "slug": "log4j", "display_name": "Log4j" },
{ "slug": "postgresql", "display_name": "PostgreSQL" }
],
"supply_chain_packages": [
{ "package": "langchain", "ecosystem": "pypi", "display_name": "LangChain" },
{ "package": "react", "ecosystem": "npm", "display_name": "React" }
],
"total": 341004
}GET /v1/cve/{cve_id}
Returns full details for a single CVE: CVSS, EPSS, KEV status, and affected Attestd product slugs. Requires a valid API key. Returns 404 when the CVE is not in Attestd's database. Returns 400 on a malformed CVE id.
Example request
curl "https://api.attestd.io/v1/cve/CVE-2021-44228" \
-H "Authorization: Bearer YOUR_API_KEY"Response
Fields include cve_id, description, cvss_score, cvss_vector, actively_exploited, remote_exploitable, authentication_required, affected_products, epss_score, epss_percentile, source_published_at, and last_checked_at.
{
"cve_id": "CVE-2021-44228",
"description": "Apache Log4j2 JNDI injection allows remote code execution.",
"cvss_score": 10.0,
"cvss_vector": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H",
"actively_exploited": true,
"remote_exploitable": true,
"authentication_required": false,
"affected_products": ["log4j"],
"epss_score": 0.97568,
"epss_percentile": 0.99976,
"source_published_at": "2021-12-10T00:00:00Z",
"last_checked_at": "2026-07-08T04:00:00Z"
}GET /v1/usage
Returns API call quota for the authenticated key: calls used this month, included cap, billing period start and end, and overage estimate. Returns 401 without a valid API key.
Example request
curl "https://api.attestd.io/v1/usage" \
-H "Authorization: Bearer YOUR_API_KEY"Response
Compare key_calls_this_month against included_calls to monitor quota. account_calls_this_month aggregates usage across all keys on the account.
{
"tier": "solo",
"key_calls_this_month": 1200,
"account_calls_this_month": 1200,
"included_calls": 10000,
"billing_period_start": "2026-07-01T00:00:00Z",
"billing_period_end": "2026-08-01T00:00:00Z",
"overage_calls": 0,
"estimated_overage_usd": 0.0
}Supply chain signals
For monitored PyPI and npm packages, the 200 OK response includes a supply_chain object with integrity data (compromised, sources, provenance, advisory_url, etc.). For CVE-only products (nginx, PostgreSQL, etc.), supply_chain is null.
See the Supply Chain Integrity guide for the full list of monitored packages and field semantics.
| Status | Meaning |
|---|---|
400 | Invalid include query parameter. Allowed value: cves |
401 | Missing, invalid, or revoked API key |
410 | Retired demo API key. detail.error is demo_key_retired |
422 | Missing or empty required query parameter (product or version) |
429 | Rate limit exceeded (see Retry-After header) |
500 | Internal server error (transient, safe to retry with backoff) |
HTTP/1.1 429 Too Many Requests
Retry-After: 60
{
"detail": "Rate limit exceeded"
}| Tier | Monthly limit | Per-minute limit |
|---|---|---|
| Free | 1,000 calls | 60/min |
| Solo | Up to 10,000 | None |
| Platform Starter | Up to 100,000 | None |
| Platform | Unlimited (contract) | None |
Monthly limits reset on your billing anniversary. Free tier (1,000 calls) returns 429 when the monthly cap or the 60/min per-minute limit is reached. Solo returns 429 at 10,000 calls. Platform Starter returns 429 at 100,000 calls. Platform has no cap under contract. The 429 response includes a Retry-After header with seconds to wait.