reference

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.io

Authentication

Pass your API key in the Authorization header as a Bearer token.

Authorization: Bearer YOUR_API_KEY

Get an API key from the developer portal. Use Authorization: Bearer atst_... on every request.

endpoint

GET /v1/check

Returns the current risk assessment for a product version.

Query parameters

ParameterDescription
productRequired. Product slug (e.g. nginx, log4j, openssh). See Quickstart for the full list.
versionRequired. Version string in any standard format (e.g. 1.24.0, 2.14.1, 8.0p1).
includeOptional. 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.
ecosystemOptional. npm or pypi. Pins a dual-listed package to one ecosystem.

Example request

bash
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.

bash
curl "https://api.attestd.io/v1/check?product=log4j&version=2.14.1&include=cves" \
  -H "Authorization: Bearer YOUR_API_KEY"
json
{
  "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)
json
{
  "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)
json
{
  "supported": false,
  "supply_chain_monitored": false,
  "typosquat": {
    "detected": true,
    "kind": "hallucination",
    "resembles": "@types/jscodeshift",
    "likely_intended": [
      "@types/jscodeshift",
      "jscodeshift"
    ],
    "confidence": 0.9,
    "ecosystem": "npm"
  }
}
batch endpoint

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

FieldDescription
itemsRequired. Array of { product, version } objects. Minimum 1, maximum 100.
items[].productProduct slug. Same format as the GET /v1/check product parameter.
items[].versionVersion string. Same format as the GET /v1/check version parameter.
items[].ecosystemOptional. 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

bash
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.

json
{
  "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):

json
{
  "product": "nginx",
  "version": "1.20.0",
  "result": null,
  "error": "internal error processing this item"
}
sbom endpoint

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.

bash
curl -X POST "https://api.attestd.io/v1/sbom" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/vnd.cyclonedx+json" \
  --data @bom.json

Response 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.

json
{
  "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
    }
  ]
}
webhooks

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.

bash
curl "https://api.attestd.io/v1/webhooks" \
  -H "Authorization: Bearer YOUR_API_KEY"
json
[
  {
    "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.

bash
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"]
  }'
json
{
  "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.

bash
curl -X DELETE "https://api.attestd.io/v1/webhooks/550e8400-e29b-41d4-a716-446655440000" \
  -H "Authorization: Bearer YOUR_API_KEY"
coverage endpoint

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

bash
curl "https://api.attestd.io/v1/coverage"

Response

json
{
  "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"
}
products endpoint

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

bash
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.

json
{
  "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
}
cve endpoint

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

bash
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.

json
{
  "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"
}
usage endpoint

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

bash
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.

json
{
  "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.

error codes
StatusMeaning
400Invalid include query parameter. Allowed value: cves
401Missing, invalid, or revoked API key
410Retired demo API key. detail.error is demo_key_retired
422Missing or empty required query parameter (product or version)
429Rate limit exceeded (see Retry-After header)
500Internal server error (transient, safe to retry with backoff)
bash
HTTP/1.1 429 Too Many Requests
Retry-After: 60

{
  "detail": "Rate limit exceeded"
}
rate limits
TierMonthly limitPer-minute limit
Free1,000 calls60/min
SoloUp to 10,000None
Platform StarterUp to 100,000None
PlatformUnlimited (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.