# Stack Facts

> Check a package before install: malware, known vulnerabilities (OSV), exploited CVEs (CISA KEV) and end-of-life, plus LLM prices; signed answers, pay per call in USDC, no account or API key. Sources: OSV.dev, CISA KEV, endoflife.date, LiteLLM and models.dev.

Free sample: `curl -s https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/eol`. Paid calls cost $0.001 to $0.05 in USDC over x402 v2 exact on Base: call the route, take the price from the 402 PAYMENT-REQUIRED header, pay and retry with PAYMENT-SIGNATURE. x402 v1 clients can pay the offer in the 402 body instead and retry with X-PAYMENT. No account or API key. Errors are never charged.

Updated 2026-10-12. Version 0.5.0. Base URL: https://g5joilnmeep4ntz3nwezxt5dmu.srv.us

## Quick start

```sh
# Free: signed samples, same shape as the paid answers
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/eol'
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/kev'
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/preview'
# Free: check an id before paying
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/validate?dataset=eol&id=nodejs'
# Paid: 402 with the price in the PAYMENT-REQUIRED header (base64 JSON) and the body
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/CVE-2021-44228'
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/check' -H 'content-type: application/json' -d '{"items":[{"product":"nodejs","version":"18.19.0"},{"product":"python","version":"3.12.4"}]}'
```

## Examples

- [curl.md](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/curl.md): curl: a free call, then a paid route's 402 and its PAYMENT-REQUIRED header decoded
- [fetch-x402.mjs](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/fetch-x402.mjs): Node 18+, no dependencies: an x402 client that pays one call (add a viem account as the signer)
- [python_x402.py](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/python_x402.py): Python 3 standard library: the same client (add an EIP-712 signer)

## Prices

USD per call, paid in USDC on Base (eip155:8453). Atomic USDC (USD x 1,000,000) is the 402 amount.

| Method | Route | Tool | USD | Atomic USDC |
|---|---|---|---|---|
| GET | `/v1/eol/{product}` | `stackfacts_eol_product` | 0.001 | 1000 |
| GET | `/v1/eol/{product}/{cycle}` | `stackfacts_eol_cycle` | 0.001 | 1000 |
| POST | `/v1/eol/check` | `stackfacts_eol_check` | 0.01 | 10000 |
| GET | `/v1/eol/check` | `stackfacts_eol_check` | 0.01 | 10000 |
| GET | `/v1/eol/image` | `stackfacts_eol_image` | 0.005 | 5000 |
| GET | `/v1/eol/snapshot` | `stackfacts_eol_snapshot` | 0.05 | 50000 |
| GET | `/v1/kev/{cve}` | `stackfacts_kev_lookup` | 0.002 | 2000 |
| POST | `/v1/kev/check` | `stackfacts_kev_check` | 0.005 | 5000 |
| GET | `/v1/kev/changes` | `stackfacts_kev_changes` | 0.01 | 10000 |
| GET | `/v1/kev/snapshot` | `stackfacts_kev_snapshot` | 0.05 | 50000 |
| GET | `/v1/models/{provider}/{model}` | `stackfacts_models_lookup` | 0.002 | 2000 |
| POST | `/v1/models/estimate` | `stackfacts_models_estimate` | 0.002 | 2000 |
| POST | `/v1/models/cheapest` | `stackfacts_models_cheapest` | 0.008 | 8000 |
| GET | `/v1/models/changes` | `stackfacts_models_changes` | 0.02 | 20000 |
| GET | `/v1/models/snapshot` | `stackfacts_models_snapshot` | 0.05 | 50000 |
| POST | `/v1/stack/check` | `stackfacts_stack_check` | 0.012 | 12000 |
| POST | `/v1/stack/watch` | `stackfacts_stack_watch` | 0.012 | 12000 |
| POST | `/v1/osv/check` | `stackfacts_osv_check` | 0.005 | 5000 |
| POST | `/v1/preflight` | `stackfacts_preflight` | 0.008 | 8000 |
| GET | `/v1/cve/{id}` | `stackfacts_cve_lookup` | 0.002 | 2000 |

## Paid tools

- [stackfacts_eol_product](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs): GET /v1/eol/{product}, $0.001. List every release cycle of a product (Node.js, Python, Ubuntu…) and if it is still supported: EOL (end-of-life) date, support status, LTS, latest version. Keywords: EOL; end-of-life dates; support status; is <product> <version> still supported; release cycles; LTS; endoflife.date.
- [stackfacts_eol_cycle](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/python/3.12): GET /v1/eol/{product}/{cycle}, $0.001. Check if a version is still supported (is python 3.12 still supported?): one release cycle, its EOL (end-of-life) date, support status, LTS, latest patch. Keywords: is <product> <version> still supported; EOL; end-of-life date; support status; support until; latest patch version; LTS.
- [stackfacts_eol_check](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/check): POST /v1/eol/check or GET /v1/eol/check, $0.01. Check dependencies, runtimes and OS versions for EOL: is each still supported? Up to 50 product@version pairs to cycle, end-of-life date and support status. Keywords: check my stack for end-of-life versions; is <product> <version> still supported; EOL; support status; dependency EOL audit; batch end-of-life check.
- [stackfacts_eol_image](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/image?image=node%3A18-alpine3.19): GET /v1/eol/image, $0.005. Check a container image for EOL: is node:18-alpine still supported? Maps its runtime, Java and base OS to end-of-life date and support status. Keywords: is my docker image end-of-life; container base image EOL; is <product> <version> still supported; Dockerfile FROM audit; EOL; support status; base OS end-of-life.
- [stackfacts_eol_snapshot](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/snapshot): GET /v1/eol/snapshot, $0.05. Download the signed EOL (end-of-life) snapshot (endoflife.date, MIT): support status of every product and cycle, still supported or not, plus its manifest. Keywords: EOL; end-of-life dataset download; support status; is <product> <version> still supported; endoflife.date bulk; signed snapshot.
- [stackfacts_kev_lookup](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/CVE-2021-44228): GET /v1/kev/{cve}, $0.002. Check if a CVE is actively exploited (CISA KEV): in_kev, date added, remediation due date, required action and known ransomware use. Keywords: check if a CVE is actively exploited (CISA KEV); known exploited vulnerability; CISA KEV lookup; CVE exploited in the wild.
- [stackfacts_kev_check](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/check): POST /v1/kev/check, $0.005. Check up to 100 CVEs at once against CISA KEV (known exploited vulnerabilities); returns in_kev and the KEV details for each one. Keywords: check if a CVE is actively exploited (CISA KEV); batch CVE check; vulnerability scan triage; known exploited vulnerabilities.
- [stackfacts_kev_changes](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/changes?since=kev-20261009T040000Z-7bd60fe8): GET /v1/kev/changes, $0.01. List CVEs added to, removed from or changed in CISA KEV since a snapshot_id, so an agent can poll for newly exploited vulnerabilities. Keywords: new CISA KEV entries; newly exploited vulnerabilities; KEV change feed; check if a CVE is actively exploited (CISA KEV).
- [stackfacts_kev_snapshot](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/snapshot): GET /v1/kev/snapshot, $0.05. Download the full signed CISA KEV snapshot: every known exploited vulnerability, with a manifest to verify the sha256 and signature. Keywords: CISA KEV download; known exploited vulnerabilities catalog; signed snapshot.
- [stackfacts_models_lookup](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/openai/gpt-4o): GET /v1/models/{provider}/{model}, $0.002. Get an LLM model's price per million tokens (input, output, cached), context window, max output, capabilities and model deprecation date. Keywords: price per million tokens; context window; model deprecation date; LLM pricing; max output tokens.
- [stackfacts_models_estimate](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/estimate): POST /v1/models/estimate, $0.002. Estimate the USD cost of an LLM call from input, output and cached token counts, exact to the micro-dollar, with the price lines used. Keywords: LLM cost estimate; price per million tokens; token cost calculator; how much will this prompt cost.
- [stackfacts_models_cheapest](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/cheapest): POST /v1/models/cheapest, $0.008. Find the cheapest model with tools and 128k context, or any limits you set: ranks the 10 cheapest LLMs by blended price per million tokens. Keywords: cheapest model with tools and 128k context; cheapest LLM; price per million tokens; context window; model comparison.
- [stackfacts_models_changes](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/changes?since=models-20261009T050000Z-044bcb8b): GET /v1/models/changes, $0.02. List LLM models added, removed or changed (price per million tokens, context window, model deprecation date) since a snapshot_id. Keywords: LLM price changes; model deprecation date; new LLM models; price per million tokens.
- [stackfacts_models_snapshot](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/snapshot): GET /v1/models/snapshot, $0.05. Download the full signed LLM model catalogue (LiteLLM + models.dev, MIT): prices, limits and capabilities, with a verifiable manifest. Keywords: LLM pricing dataset; price per million tokens; context window; signed snapshot.
- [stackfacts_stack_check](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/stack/check): POST /v1/stack/check, $0.012. Check dependencies, runtimes and OS versions in one call: OSV advisories for up to 50 package purls, EOL status for 50 product@version pairs, CISA KEV hits. Keywords: check my stack for end-of-life versions and exploited CVEs; stack audit; EOL, package vulnerabilities and KEV in one call; is <product> <version> still supported; dependency risk check; known exploited vulnerabilities.
- [stackfacts_osv_check](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/osv/check): POST /v1/osv/check, $0.005. Check dependencies for known vulnerabilities: up to 50 package purls (npm, PyPI, Go, crates.io, Maven) against OSV.dev, with fixed versions and CISA KEV flags. Keywords: check my dependencies for known vulnerabilities; is <package> <version> vulnerable; OSV; purl; npm PyPI Go crates.io Maven advisories; GHSA; fixed version; known exploited vulnerabilities; malicious package check.
- [stackfacts_preflight](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/preflight): POST /v1/preflight, $0.008. Call before installing a package an LLM suggested: one npm, PyPI or purl version against OSV, CISA KEV and EOL data. Returns ok, warn or block with reasons. Keywords: call before installing a package an LLM suggested; npm install check; pip install check; package preflight; malicious package check; known exploited vulnerabilities; OSV; purl.
- [stackfacts_cve_lookup](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/cve/CVE-2021-44228): GET /v1/cve/{id}, $0.002. Get any CVE: its CISA KEV entry, if exploited, plus every OSV advisory aliased to it, with affected packages (purls), fixed versions and CVSS. Keywords: get a CVE; CVE lookup; CVE details; CVE fixed version; CVE affected packages; CVSS score; OSV; CISA KEV.
- [stackfacts_stack_watch](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/stack/watch): POST /v1/stack/watch, $0.012. Check dependencies, runtimes and OS versions for what changed since your last call: EOL passed or near, new OSV advisories, new CISA KEV hits. Returns a token. Keywords: watch my stack for end-of-life and new exploited CVEs; Use when you need to know what changed in a stack since your last check; stack monitor; dependency monitor; what changed since my last check; alert when a version reaches end of life; new vulnerabilities in my packages.

`GET /v1/cve/{id}` still answers (and is charged) when one source is stale or missing: a stale OSV ecosystem's advisories are left out and listed in `meta.datasets.osv.stale_ecosystems`; a stale or missing KEV snapshot gives `in_kev: null` (not checked). Each left-out source is named in `meta.warnings` (`{code, dataset, message}`). It answers 503 (uncharged) only when KEV and every OSV ecosystem are unusable.

## Free tools

- [stackfacts_preview](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/preview): GET /v1/{dataset}/preview, free. Preview a dataset for free: sample end-of-life products, the newest CISA KEV entries or LLM prices, signed like the paid answers. Keywords: free sample; end-of-life dates; CISA KEV; price per million tokens.
- [stackfacts_try](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/kev): GET /v1/try/{dataset}, free. Try a paid answer for free on a fixed example: nodejs end-of-life cycles, CVE-2021-44228 in CISA KEV, or openai/gpt-4o prices. Keywords: free example; try before paying; check if a CVE is actively exploited (CISA KEV).
- [stackfacts_index](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/index): GET /v1/{dataset}/index, free. List the valid ids for free: end-of-life product names and aliases, or LLM provider/model ids, so a paid call never misses. Keywords: product list; model list; valid ids; aliases.
- [stackfacts_validate](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/validate?dataset=eol&id=nodejs): GET /v1/validate, free. Validate an id for free before paying: whether a product, product/cycle or provider/model exists, or a CVE id is well formed. Keywords: validate id; check before paying; product exists.
- [stackfacts_manifest](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/manifest): GET /v1/{dataset}/manifest, free. Get the signed manifest of a dataset's latest snapshot for free: snapshot_id, sha256, record count, source, licence and signature. Keywords: signed manifest; snapshot_id; data freshness; provenance.
- [stackfacts_keys](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/keys): GET /v1/keys, free. Get the Ed25519 public keys (JWK) that verify the signature on every answer and snapshot manifest. Keywords: public key; verify signature; JWKS; Ed25519.

## Stack watch

`POST /v1/stack/watch` ($0.012 a call, start or re-check) answers what changed in a stack since your last call. Start with a stack, as for `POST /v1/stack/check`: the answer holds the full check and a signed token, `data.watch.token`. Later send `{"token": "<watch.token>"}`: the answer lists each change since then (an end-of-life date passed or near, a CVE newly in CISA KEV, a new OSV advisory) with `worst` (high, medium, low or null for no change) and a new token for the next call.

- The token holds your stack in readable form; keep it as you would the stack. It is signed, so a changed token is refused (token_invalid), and it is good for 180 days.
- No watch state is stored: the token is the whole watch. Lose it and you start again.
- Data refreshes every 4 h (KEV), 6 h (OSV) and 24 h (EOL), so a sooner re-check finds nothing new; `watch.suggested_recheck_at` is the next refresh that matters.
- A start token is at most 32,000 characters and a re-check token at most 48,000; over that the call is 413 too_many_items, not charged: split the stack into two watches.

## Datasets

- Software end-of-life dates (`/v1/eol/…`): endoflife.date, MIT, refreshed every 24 h. Snapshot `eol-20261011T184823Z-fb3e9d0a`, as of 2026-10-11T18:48:23Z, 483 records. Free sample: [/v1/try/eol](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/eol).
- Actively exploited CVEs (CISA KEV) (`/v1/kev/…`): CISA KEV, CC0-1.0, refreshed every 4 h. Snapshot `kev-20261009T183031Z-bc476d6c`, as of 2026-10-09T18:30:31Z, 1739 records. Free sample: [/v1/try/kev](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/kev).
- LLM prices, context windows and deprecation dates (`/v1/models/…`): LiteLLM + models.dev, MIT, refreshed every 12 h. Snapshot `models-20261010T070852Z-7d710157`, as of 2026-10-10T07:08:52Z, 368 records. Free sample: [/v1/try/models](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/models).

Data is provided as is, without warranty of any kind. Payments are final. Every dataset is normalised (modified) from the sources named here, and no source endorses this service.

## Machine-readable

- [openapi.json](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/openapi.json): OpenAPI 3.1 with x-payment-info on every paid operation
- [pricing.json](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/pricing.json): paid routes and prices
- [/.well-known/x402](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/.well-known/x402): paid resources (x402scan and IETF draft shapes)
- [/.well-known/api-catalog](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/.well-known/api-catalog): RFC 9727 linkset
- [MCP server card](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/.well-known/mcp/server-card.json): the same tools over MCP (Streamable HTTP) at `POST https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/mcp`
- [/v1/keys](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/keys): Ed25519 public keys (JWK) for verifying answers and snapshots

## Docs

- [End-of-life pages](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/eol/nodejs): is a product end-of-life? One page per product at /docs/eol/<product> (483 products, all in sitemap.xml)
- [Newest KEV CVEs](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/kev-recent): the 20 newest actively exploited CVEs
- [LLM model changes](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/models-changes): price and deprecation changes in the last 7 days
- [Cheapest LLMs by context size](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/models-cheapest): the 5 cheapest models with at least 32k, 128k, 200k, 1M tokens of context
- [MCP setup](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/mcp): copy-paste MCP configs for Claude Code, Cursor and Codex
- [Compared with the free sources](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/compare): what each free source gives and what Stack Facts adds
- [Errors](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors): error codes and what to do
- [Provenance](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/provenance): sources, refresh schedule and snapshot verification
- [Licences](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/licences): licences and attribution for every source
- [Changelog](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/changelog): service changes
- [Health](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/health): status and freshness of each dataset

## How to pay (x402)

- Call the route. Without a `PAYMENT-SIGNATURE` header it answers 402 before reading the input: the `PAYMENT-REQUIRED` header holds the offer as base64 JSON, and the body holds the same JSON.
- The offer is x402 v2: `accepts[0]` has scheme `exact`, network `eip155:8453` (Base), asset USDC `0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913`, the amount in atomic units (6 decimals) and payTo.
- Sign the payment for `accepts[0]` with any x402 v2 client and send the same request again with `PAYMENT-SIGNATURE` (base64 JSON of the payment payload).
- The answer is a 200 with `data` and `meta` and a `PAYMENT-RESPONSE` header (base64 JSON settle result). Payment settles only after the answer is ready; a 4xx or 5xx is never settled.
- Optional: with the payment-identifier extension, the same id and payment return the stored answer for 24 hours without a second charge.

## Every answer

Every 200 (free or paid) is `{"data": …, "meta": …}`. meta holds:

- `dataset`, `snapshot_id`: which signed snapshot answered
- `as_of`: when the source data was fetched; `retrieved_at`: when the source was last checked and still matched
- `stale`: true once the dataset is older than twice its refresh cadence (paid routes then answer 503 stale_data)
- OSV meta (`meta.datasets.osv` on /v1/stack/check): when no package in the call uses a loaded ecosystem, `stale` is false and `retrieved_at`, `age_hours` and `stale_at` are null
- `source`, `licence`, `attribution`, `notice`: source and terms
- `sha256`, `signature`, `key_id`, `manifest_url`: the signed snapshot manifest

## Tool reference

### stackfacts_eol_product

List every release cycle of a product (Node.js, Python, Ubuntu…) and if it is still supported: EOL (end-of-life) date, support status, LTS, latest version.

`GET /v1/eol/{product}`: $0.001 per call (1000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "product": {
      "type": "string",
      "description": "endoflife.date product id or alias, e.g. nodejs, python, ubuntu (GET /v1/eol/index lists them)",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "product"
  ],
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "product": "nodejs",
    "label": "Node.js",
    "cycles": [
      {
        "cycle": "24",
        "eol": "2028-04-30",
        "latest": "24.21.0",
        "latest_date": "2026-09-08",
        "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V24.md#24.21.0",
        "lts": "2025-10-28",
        "release_date": "2025-05-06",
        "support_until": "2026-10-20",
        "status": "supported",
        "days_until_eol": 569,
        "is_lts": true
      },
      {
        "cycle": "22",
        "eol": "2027-04-30",
        "latest": "22.23.3",
        "latest_date": "2026-09-23",
        "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V22.md#22.23.3",
        "lts": "2024-10-29",
        "release_date": "2024-04-24",
        "support_until": "2025-10-21",
        "status": "security-only",
        "days_until_eol": 203,
        "is_lts": true
      },
      {
        "cycle": "18",
        "eol": "2025-04-30",
        "latest": "18.20.8",
        "latest_date": "2025-03-27",
        "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#18.20.8",
        "lts": "2022-10-25",
        "release_date": "2022-04-19",
        "support_until": "2023-10-18",
        "status": "eol",
        "days_until_eol": -527,
        "is_lts": true
      }
    ]
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_eol_cycle

Check if a version is still supported (is python 3.12 still supported?): one release cycle, its EOL (end-of-life) date, support status, LTS, latest patch.

`GET /v1/eol/{product}/{cycle}`: $0.001 per call (1000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/python/3.12'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "product": {
      "type": "string",
      "description": "endoflife.date product id or alias, e.g. nodejs, python, ubuntu (GET /v1/eol/index lists them)",
      "minLength": 1,
      "maxLength": 200
    },
    "cycle": {
      "type": "string",
      "description": "Release cycle, e.g. 3.12, 22 or 24.04",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "product",
    "cycle"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "product": "python",
    "label": "Python",
    "cycle": "3.12",
    "eol": "2028-10-31",
    "latest": "3.12.15",
    "latest_date": "2026-10-01",
    "link": "https://www.python.org/downloads/release/python-31215/",
    "lts": false,
    "release_date": "2023-10-02",
    "support_until": "2025-04-02",
    "status": "security-only",
    "days_until_eol": 753,
    "is_lts": false
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_eol_check

Check dependencies, runtimes and OS versions for EOL: is each still supported? Up to 50 product@version pairs to cycle, end-of-life date and support status.

`POST /v1/eol/check`: $0.01 per call (10000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/check' -H 'content-type: application/json' -d '{"items":[{"product":"nodejs","version":"18.19.0"},{"product":"python","version":"3.12.4"}]}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "description": "The versions to check, in order",
      "items": {
        "type": "object",
        "properties": {
          "product": {
            "type": "string",
            "description": "endoflife.date product id or alias, e.g. nodejs, python, ubuntu (GET /v1/eol/index lists them)",
            "minLength": 1,
            "maxLength": 200
          },
          "version": {
            "type": "string",
            "description": "Version string, e.g. 18.19.0 (send \"3.10\" as a string, not a number)",
            "minLength": 1,
            "maxLength": 200
          }
        },
        "required": [
          "product",
          "version"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}
```

`GET /v1/eol/check`: $0.01 per call (10000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/check?items=nodejs%4018.19.0%2Cpython%403.12.4'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "string",
      "description": "Comma-separated product@version pairs, at most 50, e.g. nodejs@18.19.0,python@3.12.4",
      "minLength": 3
    }
  },
  "required": [
    "items"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "count": 2,
    "matched": 2,
    "unknown_count": 0,
    "results": [
      {
        "index": 0,
        "product": "nodejs",
        "version": "18.19.0",
        "found": true,
        "resolved": "nodejs",
        "label": "Node.js",
        "match": {
          "cycle": "18",
          "eol": "2025-04-30",
          "latest": "18.20.8",
          "latest_date": "2025-03-27",
          "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#18.20.8",
          "lts": "2022-10-25",
          "release_date": "2022-04-19",
          "support_until": "2023-10-18",
          "status": "eol",
          "days_until_eol": -527,
          "is_lts": true
        }
      },
      {
        "index": 1,
        "product": "python",
        "version": "3.12.4",
        "found": true,
        "resolved": "python",
        "label": "Python",
        "match": {
          "cycle": "3.12",
          "eol": "2028-10-31",
          "latest": "3.12.15",
          "latest_date": "2026-10-01",
          "link": "https://www.python.org/downloads/release/python-31215/",
          "lts": false,
          "release_date": "2023-10-02",
          "support_until": "2025-04-02",
          "status": "security-only",
          "days_until_eol": 753,
          "is_lts": false
        }
      }
    ],
    "unknown": []
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_eol_image

Check a container image for EOL: is node:18-alpine still supported? Maps its runtime, Java and base OS to end-of-life date and support status.

`GET /v1/eol/image`: $0.005 per call (5000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/image?image=node%3A18-alpine3.19'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "image": {
      "type": "string",
      "description": "Image reference or a whole Dockerfile FROM line (FROM, --platform and AS name are dropped), e.g. node:18-alpine3.19, python:3.12-slim-bookworm, gcr.io/distroless/nodejs20-debian12, FROM --platform=linux/amd64 ubuntu:22.04 AS build",
      "minLength": 1,
      "maxLength": 256
    }
  },
  "required": [
    "image"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "image": "node:18-alpine3.19",
    "registry": "docker.io",
    "repository": "node",
    "tag": "18-alpine3.19",
    "digest": null,
    "status": "eol",
    "days_until_eol": -527,
    "matched": 2,
    "complete": true,
    "components": [
      {
        "role": "image",
        "product": "nodejs",
        "version": "18",
        "matched_by": "known_image",
        "found": true,
        "resolved": "nodejs",
        "label": "Node.js",
        "match": {
          "cycle": "18",
          "eol": "2025-04-30",
          "latest": "18.20.8",
          "latest_date": "2025-03-27",
          "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#18.20.8",
          "lts": "2022-10-25",
          "release_date": "2022-04-19",
          "support_until": "2023-10-18",
          "status": "eol",
          "days_until_eol": -527,
          "is_lts": true
        }
      },
      {
        "role": "base_os",
        "product": "alpine-linux",
        "version": "3.19",
        "found": true,
        "resolved": "alpine-linux",
        "label": "Alpine Linux",
        "match": {
          "cycle": "3.19",
          "eol": "2025-11-01",
          "latest": "3.19.9",
          "latest_date": "2025-10-08",
          "link": "https://alpinelinux.org/posts/Alpine-3.17.10-3.18.9-3.19.4-3.20.3-released.html",
          "lts": false,
          "release_date": "2023-12-07",
          "support_until": null,
          "status": "eol",
          "days_until_eol": -342,
          "is_lts": false
        }
      }
    ]
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "y-swWAWNFw94iUOowlaeykuDgZgokmTaN4yousqQUqrlvFjDqnrPeCZVSjamXO_s7vAk1LGVXDA05tW97TcdCw",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_eol_snapshot

Download the signed EOL (end-of-life) snapshot (endoflife.date, MIT): support status of every product and cycle, still supported or not, plus its manifest.

`GET /v1/eol/snapshot`: $0.05 per call (50000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/snapshot'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "manifest": {
      "dataset": "eol",
      "version": 1,
      "snapshot_id": "eol-20261009T060000Z-d61970d6",
      "fetched_at": "2026-10-09T06:00:00Z",
      "source_url": "https://endoflife.date",
      "licence": "MIT",
      "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
      "upstream_etag": null,
      "record_count": 16,
      "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
      "prev_snapshot_id": null,
      "key_id": "sf-test-1",
      "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA"
    },
    "snapshot": {
      "dataset": "eol",
      "meta": {
        "upstream_schema_version": "1.2.1"
      },
      "records": [
        {
          "cycles": [
            {
              "cycle": "24",
              "eol": "2028-04-30",
              "latest": "24.21.0",
              "latest_date": "2026-09-08",
              "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V24.md#24.21.0",
              "lts": "2025-10-28",
              "release_date": "2025-05-06",
              "support_until": "2026-10-20"
            },
            {
              "cycle": "22",
              "eol": "2027-04-30",
              "latest": "22.23.3",
              "latest_date": "2026-09-23",
              "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V22.md#22.23.3",
              "lts": "2024-10-29",
              "release_date": "2024-04-24",
              "support_until": "2025-10-21"
            }
          ],
          "label": "Node.js",
          "product": "nodejs"
        }
      ],
      "version": 1
    },
    "licence": {
      "spdx": "MIT",
      "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
      "source": "https://raw.githubusercontent.com/endoflife-date/endoflife.date/master/LICENSE",
      "text": "Copyright 2020 endoflife.date contributors\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the \"Software\"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n",
      "sources": [
        "endoflife.date"
      ]
    },
    "verify": "sha256 of the canonical JSON (RFC 8785) of data.snapshot equals data.manifest.sha256; data.manifest.signature is Ed25519 (base64url) over the canonical JSON of the manifest without \"signature\", by the key in GET /v1/keys with that key_id"
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_kev_lookup

Check if a CVE is actively exploited (CISA KEV): in_kev, date added, remediation due date, required action and known ransomware use.

`GET /v1/kev/{cve}`: $0.002 per call (2000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/CVE-2021-44228'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "cve": {
      "type": "string",
      "description": "CVE id, e.g. CVE-2021-44228",
      "pattern": "^[Cc][Vv][Ee]-\\d{4}-\\d{4,}$",
      "maxLength": 32
    }
  },
  "required": [
    "cve"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "cve": "CVE-2021-44228",
    "in_kev": true,
    "vendor": "Apache",
    "product": "Log4j2",
    "name": "Apache Log4j2 Remote Code Execution Vulnerability",
    "date_added": "2021-12-10",
    "due_date": "2021-12-24",
    "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
    "known_ransomware": true,
    "notes_urls": [
      "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
    ],
    "catalog_version": "2026.10.10"
  },
  "meta": {
    "dataset": "kev",
    "snapshot_id": "kev-20261009T060000Z-f4586cc5",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
    "licence": "CC0-1.0",
    "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
    "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
    "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/kev/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_kev_check

Check up to 100 CVEs at once against CISA KEV (known exploited vulnerabilities); returns in_kev and the KEV details for each one.

`POST /v1/kev/check`: $0.005 per call (5000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/check' -H 'content-type: application/json' -d '{"cves":["CVE-2021-44228","CVE-1999-0001"]}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "cves": {
      "type": "array",
      "minItems": 1,
      "maxItems": 100,
      "items": {
        "type": "string",
        "description": "CVE id, e.g. CVE-2021-44228",
        "pattern": "^[Cc][Vv][Ee]-\\d{4}-\\d{4,}$",
        "maxLength": 32
      },
      "description": "CVE ids; duplicates are merged"
    }
  },
  "required": [
    "cves"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "count": 2,
    "in_kev": 1,
    "catalog_version": "2026.10.10",
    "results": [
      {
        "cve": "CVE-2021-44228",
        "in_kev": true,
        "vendor": "Apache",
        "product": "Log4j2",
        "name": "Apache Log4j2 Remote Code Execution Vulnerability",
        "date_added": "2021-12-10",
        "due_date": "2021-12-24",
        "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
        "known_ransomware": true,
        "notes_urls": [
          "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
        ]
      },
      {
        "cve": "CVE-1999-0001",
        "in_kev": false
      }
    ]
  },
  "meta": {
    "dataset": "kev",
    "snapshot_id": "kev-20261009T060000Z-f4586cc5",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
    "licence": "CC0-1.0",
    "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
    "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
    "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/kev/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_kev_changes

List CVEs added to, removed from or changed in CISA KEV since a snapshot_id, so an agent can poll for newly exploited vulnerabilities.

`GET /v1/kev/changes`: $0.01 per call (10000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/changes?since=kev-20261009T040000Z-7bd60fe8'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "since": {
      "type": "string",
      "description": "meta.snapshot_id of an earlier kev answer, e.g. kev-20261009T060000Z-1a2b3c4d; kept for 30 snapshots",
      "pattern": "^kev-\\d{8}T\\d{6}Z-[0-9a-f]{8}$",
      "maxLength": 64
    }
  },
  "required": [
    "since"
  ],
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "since": "kev-20261009T040000Z-7bd60fe8",
    "to": "kev-20261009T060000Z-f4586cc5",
    "snapshots_merged": 2,
    "counts": {
      "added": 2,
      "removed": 1,
      "modified": 3
    },
    "added": [],
    "removed": [
      "CVE-2009-3459"
    ],
    "modified": [
      {
        "cve": "CVE-2023-34362",
        "in_kev": true,
        "vendor": "Progress",
        "product": "MOVEit Transfer",
        "name": "Progress MOVEit Transfer SQL Injection Vulnerability",
        "date_added": "2023-06-02",
        "due_date": "2023-06-30",
        "required_action": "Apply updates per vendor instructions.",
        "known_ransomware": true,
        "notes_urls": [
          "https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-158a",
          "https://community.progress.com/s/article/MOVEit-Transfer-Critical-Vulnerability-31May2023",
          "https://nvd.nist.gov/vuln/detail/CVE-2023-34362"
        ]
      }
    ]
  },
  "meta": {
    "dataset": "kev",
    "snapshot_id": "kev-20261009T060000Z-f4586cc5",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
    "licence": "CC0-1.0",
    "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
    "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
    "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/kev/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_kev_snapshot

Download the full signed CISA KEV snapshot: every known exploited vulnerability, with a manifest to verify the sha256 and signature.

`GET /v1/kev/snapshot`: $0.05 per call (50000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/snapshot'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "manifest": {
      "dataset": "kev",
      "version": 1,
      "snapshot_id": "kev-20261009T060000Z-f4586cc5",
      "fetched_at": "2026-10-09T06:00:00Z",
      "source_url": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
      "licence": "CC0-1.0",
      "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
      "upstream_etag": null,
      "record_count": 31,
      "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
      "prev_snapshot_id": "kev-20261009T050000Z-013e1b3f",
      "key_id": "sf-test-1",
      "upstream_meta": {
        "catalogVersion": "2026.10.10",
        "dateReleased": "2026-10-10T02:00:00.0000Z"
      },
      "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA"
    },
    "snapshot": {
      "dataset": "kev",
      "records": [
        {
          "cve": "CVE-2021-44228",
          "date_added": "2021-12-10",
          "due_date": "2021-12-24",
          "known_ransomware": true,
          "name": "Apache Log4j2 Remote Code Execution Vulnerability",
          "notes_urls": [
            "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
          ],
          "product": "Log4j2",
          "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
          "vendor": "Apache"
        }
      ],
      "version": 1
    },
    "licence": {
      "spdx": "CC0-1.0",
      "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
      "source": "https://www.cisa.gov/sites/default/files/licenses/kev/license.txt",
      "text": "The KEV database is distributed under the Creative Commons 0 1.0 License. You may use this data in any legal manner but note that information provided at any 3rd party links included in the KEV database are bound by the policies and licenses of those 3rd party websites. Use of the information does not authorize you to use the CISA Logo or DHS Seal, nor should such use be interpreted as an endorsement by CISA or DHS.\n\nCC0 1.0 Universal legal code: https://creativecommons.org/publicdomain/zero/1.0/legalcode\n",
      "sources": [
        "CISA KEV"
      ]
    },
    "verify": "sha256 of the canonical JSON (RFC 8785) of data.snapshot equals data.manifest.sha256; data.manifest.signature is Ed25519 (base64url) over the canonical JSON of the manifest without \"signature\", by the key in GET /v1/keys with that key_id"
  },
  "meta": {
    "dataset": "kev",
    "snapshot_id": "kev-20261009T060000Z-f4586cc5",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
    "licence": "CC0-1.0",
    "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
    "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
    "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/kev/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_models_lookup

Get an LLM model's price per million tokens (input, output, cached), context window, max output, capabilities and model deprecation date.

`GET /v1/models/{provider}/{model}`: $0.002 per call (2000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/openai/gpt-4o'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "description": "Provider, e.g. openai, anthropic, google (GET /v1/models/index lists them)",
      "minLength": 1,
      "maxLength": 100
    },
    "model": {
      "type": "string",
      "description": "Model id at the provider, e.g. gpt-4o",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "provider",
    "model"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "aliases": [],
    "cache_read_usd_per_mtok": 1.25,
    "cache_write_usd_per_mtok": null,
    "context_tokens": 128000,
    "deprecation_date": null,
    "id": "openai/gpt-4o",
    "input_modalities": [
      "text",
      "image",
      "pdf"
    ],
    "input_usd_per_mtok": 2,
    "knowledge_cutoff": "2023-09",
    "max_output_tokens": 16384,
    "mode": "chat",
    "model": "gpt-4o",
    "name": "GPT-4o",
    "output_modalities": [
      "text"
    ],
    "output_usd_per_mtok": 10,
    "provider": "openai",
    "reasoning": false,
    "release_date": "2024-05-13",
    "sources": {
      "litellm": true,
      "modelsdev": true
    },
    "sources_disagree": [
      "input_usd_per_mtok"
    ],
    "status": null,
    "tool_call": true,
    "vision": true,
    "deprecated": false,
    "days_until_deprecation": null
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_models_estimate

Estimate the USD cost of an LLM call from input, output and cached token counts, exact to the micro-dollar, with the price lines used.

`POST /v1/models/estimate`: $0.002 per call (2000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/estimate' -H 'content-type: application/json' -d '{"model":"openai/gpt-4o","input_tokens":1000,"output_tokens":200,"cached_tokens":0}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "model": {
      "type": "string",
      "description": "provider/model id, e.g. openai/gpt-4o",
      "minLength": 1,
      "maxLength": 200
    },
    "input_tokens": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000000,
      "description": "Uncached input tokens"
    },
    "output_tokens": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000000,
      "description": "Output tokens"
    },
    "cached_tokens": {
      "type": "integer",
      "minimum": 0,
      "maximum": 10000000000,
      "description": "Cached input tokens (cache reads)"
    }
  },
  "required": [
    "model"
  ],
  "anyOf": [
    {
      "required": [
        "input_tokens"
      ]
    },
    {
      "required": [
        "output_tokens"
      ]
    },
    {
      "required": [
        "cached_tokens"
      ]
    }
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "model": "openai/gpt-4o",
    "name": "GPT-4o",
    "lines": [
      {
        "kind": "input",
        "tokens": 1000,
        "price_field": "input_usd_per_mtok",
        "usd_per_mtok": 2,
        "cost_usd": "0.002"
      },
      {
        "kind": "output",
        "tokens": 200,
        "price_field": "output_usd_per_mtok",
        "usd_per_mtok": 10,
        "cost_usd": "0.002"
      },
      {
        "kind": "cached",
        "tokens": 0,
        "price_field": "cache_read_usd_per_mtok",
        "usd_per_mtok": 1.25,
        "cost_usd": "0"
      }
    ],
    "cost_usd": "0.004",
    "cost_micro_usd": 4000,
    "formula": "cost_usd = (input_tokens × input + output_tokens × output + cached_tokens × cached) / 1,000,000, prices in USD per million tokens; input_tokens exclude cached_tokens; cost_usd is exact, cost_micro_usd is it rounded half up to the micro-dollar",
    "notes": [],
    "deprecated": false,
    "days_until_deprecation": null,
    "deprecation_date": null,
    "sources_disagree": [
      "input_usd_per_mtok"
    ]
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_models_cheapest

Find the cheapest model with tools and 128k context, or any limits you set: ranks the 10 cheapest LLMs by blended price per million tokens.

`POST /v1/models/cheapest`: $0.008 per call (8000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/cheapest' -H 'content-type: application/json' -d '{"min_context":128000,"tools":true}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "min_context": {
      "type": "integer",
      "minimum": 1,
      "description": "Minimum context window in tokens"
    },
    "tools": {
      "type": "boolean",
      "description": "Require tool calling"
    },
    "vision": {
      "type": "boolean",
      "description": "Require image input"
    },
    "max_usd_per_mtok": {
      "type": "number",
      "minimum": 0,
      "description": "Max blended USD per million tokens (3:1 input:output)"
    },
    "providers": {
      "type": "array",
      "minItems": 1,
      "maxItems": 20,
      "items": {
        "type": "string",
        "maxLength": 40
      },
      "description": "Only these providers"
    },
    "include_deprecated": {
      "type": "boolean",
      "description": "Keep deprecated models (default false)"
    },
    "include_free": {
      "type": "boolean",
      "description": "Keep models priced 0/0 (default false)"
    }
  },
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "results": [
      {
        "rank": 1,
        "id": "cohere/command-r-08-2024",
        "name": "Command R",
        "blended_usd_per_mtok": 0.2625,
        "input_usd_per_mtok": 0.15,
        "output_usd_per_mtok": 0.6,
        "cache_read_usd_per_mtok": null,
        "context_tokens": 128000,
        "max_output_tokens": 4000,
        "tool_call": true,
        "vision": false,
        "reasoning": false,
        "deprecated": false,
        "days_until_deprecation": null,
        "sources_disagree": []
      },
      {
        "rank": 2,
        "id": "deepseek/deepseek-flash",
        "name": "DeepSeek V4.1 Flash",
        "blended_usd_per_mtok": 0.2625,
        "input_usd_per_mtok": 0.15,
        "output_usd_per_mtok": 0.6,
        "cache_read_usd_per_mtok": 0.003,
        "context_tokens": 1000000,
        "max_output_tokens": 393216,
        "tool_call": true,
        "vision": true,
        "reasoning": true,
        "deprecated": false,
        "days_until_deprecation": null,
        "sources_disagree": []
      }
    ],
    "filter": {
      "applied": {
        "min_context": 128000,
        "tools": true,
        "vision": false,
        "max_usd_per_mtok": null,
        "providers": null,
        "include_deprecated": false,
        "include_free": false
      },
      "considered": 36,
      "matched": 24,
      "returned": 10,
      "excluded": {
        "provider": 0,
        "not_text": 1,
        "no_price": 0,
        "free": 1,
        "deprecated": 10,
        "context": 0,
        "tools": 0,
        "vision": 0,
        "price": 0
      },
      "reasons": {
        "provider": "provider is not in the providers list",
        "not_text": "mode is not text generation (chat, responses, completion)",
        "no_price": "no input or output price in the sources",
        "free": "input and output price are both 0 (free preview or unfilled price; send include_free:true to keep them)",
        "deprecated": "deprecated now (send include_deprecated:true to keep them)",
        "context": "context_tokens unknown or below min_context",
        "tools": "tool calling not confirmed (tools:true)",
        "vision": "image input not confirmed (vision:true)",
        "price": "blended price above max_usd_per_mtok"
      }
    },
    "ranking": "blended_usd_per_mtok = (3 × input + 1 × output) / 4 USD per million tokens (a 3:1 input:output token mix), cheapest first, ties by id; top 10"
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_models_changes

List LLM models added, removed or changed (price per million tokens, context window, model deprecation date) since a snapshot_id.

`GET /v1/models/changes`: $0.02 per call (20000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/changes?since=models-20261009T050000Z-044bcb8b'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "since": {
      "type": "string",
      "description": "meta.snapshot_id of an earlier models answer, e.g. models-20261009T060000Z-1a2b3c4d; kept for 30 snapshots",
      "pattern": "^models-\\d{8}T\\d{6}Z-[0-9a-f]{8}$",
      "maxLength": 64
    }
  },
  "required": [
    "since"
  ],
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "since": "models-20261009T050000Z-044bcb8b",
    "to": "models-20261009T060000Z-f99d55c9",
    "snapshots_merged": 1,
    "counts": {
      "added": 1,
      "removed": 1,
      "modified": 2
    },
    "added": [],
    "removed": [
      "perplexity/sonar"
    ],
    "modified": [
      {
        "aliases": [],
        "cache_read_usd_per_mtok": 0.075,
        "cache_write_usd_per_mtok": null,
        "context_tokens": 128000,
        "deprecation_date": "2026-10-01",
        "id": "openai/gpt-4o-mini",
        "input_modalities": [
          "text",
          "image",
          "pdf"
        ],
        "input_usd_per_mtok": 0.15,
        "knowledge_cutoff": "2023-09",
        "max_output_tokens": 16384,
        "mode": "chat",
        "model": "gpt-4o-mini",
        "name": "GPT-4o mini",
        "output_modalities": [
          "text"
        ],
        "output_usd_per_mtok": 0.6,
        "provider": "openai",
        "reasoning": false,
        "release_date": "2024-07-18",
        "sources": {
          "litellm": true,
          "modelsdev": true
        },
        "sources_disagree": [],
        "status": null,
        "tool_call": true,
        "vision": true,
        "deprecated": true,
        "days_until_deprecation": -8
      }
    ]
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_models_snapshot

Download the full signed LLM model catalogue (LiteLLM + models.dev, MIT): prices, limits and capabilities, with a verifiable manifest.

`GET /v1/models/snapshot`: $0.05 per call (50000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/snapshot'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "manifest": {
      "dataset": "models",
      "version": 1,
      "snapshot_id": "models-20261009T060000Z-f99d55c9",
      "fetched_at": "2026-10-09T06:00:00Z",
      "source_url": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
      "licence": "MIT",
      "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
      "upstream_etag": {
        "litellm": null,
        "modelsdev": null
      },
      "record_count": 36,
      "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
      "prev_snapshot_id": "models-20261009T050000Z-044bcb8b",
      "key_id": "sf-test-1",
      "upstream_meta": {
        "litellm": {
          "bytes": 34408,
          "entries": 39,
          "kept": 26,
          "skipped": {
            "provider_not_in_scope": 3,
            "duplicate_id": 3,
            "finetune": 1,
            "mode_not_priced_per_token": 4,
            "id_not_supported": 2,
            "sample_spec": 1
          }
        },
        "modelsdev": {
          "bytes": 25204,
          "providers": 13,
          "models": 37,
          "kept": 28,
          "skipped": {
            "provider_not_in_scope": 2,
            "not_text_to_text": 6,
            "id_not_supported": 1
          }
        },
        "merged": {
          "both": 18,
          "litellm_only": 8,
          "modelsdev_only": 10,
          "disagree": 8
        }
      },
      "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ"
    },
    "snapshot": {
      "dataset": "models",
      "records": [
        {
          "aliases": [],
          "cache_read_usd_per_mtok": 1.25,
          "cache_write_usd_per_mtok": null,
          "context_tokens": 128000,
          "deprecation_date": null,
          "id": "openai/gpt-4o",
          "input_modalities": [
            "text",
            "image",
            "pdf"
          ],
          "input_usd_per_mtok": 2,
          "knowledge_cutoff": "2023-09",
          "max_output_tokens": 16384,
          "mode": "chat",
          "model": "gpt-4o",
          "name": "GPT-4o",
          "output_modalities": [
            "text"
          ],
          "output_usd_per_mtok": 10,
          "provider": "openai",
          "reasoning": false,
          "release_date": "2024-05-13",
          "sources": {
            "litellm": true,
            "modelsdev": true
          },
          "sources_disagree": [
            "input_usd_per_mtok"
          ],
          "status": null,
          "tool_call": true,
          "vision": true
        }
      ],
      "version": 1
    },
    "licence": {
      "spdx": "MIT",
      "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
      "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/LICENSE (LiteLLM, MIT); https://raw.githubusercontent.com/anomalyco/models.dev/dev/LICENSE (models.dev, MIT)",
      "text": "LiteLLM (https://raw.githubusercontent.com/BerriAI/litellm/main/LICENSE):\n\nPortions of this software are licensed as follows:\n\n* All content that resides under the \"enterprise/\" directory of this repository, if that directory exists, is licensed under the license defined in \"enterprise/LICENSE\".\n* Content outside of the above mentioned directories or restrictions above is available under the MIT license as defined below.\n---\nMIT License\n\nCopyright (c) 2023 Berri AI\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n----------\n\nmodels.dev (https://raw.githubusercontent.com/anomalyco/models.dev/dev/LICENSE):\n\nMIT License\n\nCopyright (c) 2025 models.dev\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n",
      "sources": [
        "LiteLLM",
        "models.dev"
      ]
    },
    "verify": "sha256 of the canonical JSON (RFC 8785) of data.snapshot equals data.manifest.sha256; data.manifest.signature is Ed25519 (base64url) over the canonical JSON of the manifest without \"signature\", by the key in GET /v1/keys with that key_id"
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_stack_check

Check dependencies, runtimes and OS versions in one call: OSV advisories for up to 50 package purls, EOL status for 50 product@version pairs, CISA KEV hits.

`POST /v1/stack/check`: $0.012 per call (12000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/stack/check' -H 'content-type: application/json' -d '{"items":["nodejs@18.19.0","python@3.12.4"],"cves":["CVE-1999-0001","CVE-2021-44228"],"packages":["pkg:npm/lodash@4.17.20"]}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "items": {
      "type": "array",
      "minItems": 0,
      "maxItems": 50,
      "description": "The versions to check, in order: \"product@version\" strings or {product, version} objects. Needed unless packages is sent",
      "items": {
        "anyOf": [
          {
            "type": "string",
            "description": "product@version, e.g. nodejs@18.19.0 (GET /v1/eol/index lists products)",
            "pattern": "^[^@]+@.+$",
            "minLength": 3,
            "maxLength": 401
          },
          {
            "type": "object",
            "properties": {
              "product": {
                "type": "string",
                "description": "endoflife.date product id or alias, e.g. nodejs, python, ubuntu (GET /v1/eol/index lists them)",
                "minLength": 1,
                "maxLength": 200
              },
              "version": {
                "type": "string",
                "description": "Version string, e.g. 18.19.0 (send \"3.10\" as a string, not a number)",
                "minLength": 1,
                "maxLength": 200
              }
            },
            "required": [
              "product",
              "version"
            ],
            "additionalProperties": false
          }
        ]
      }
    },
    "cves": {
      "type": "array",
      "minItems": 0,
      "maxItems": 100,
      "items": {
        "type": "string",
        "description": "CVE id, e.g. CVE-2021-44228",
        "pattern": "^[Cc][Vv][Ee]-\\d{4}-\\d{4,}$",
        "maxLength": 32
      },
      "description": "Optional CVE ids to check against CISA KEV; duplicates are merged. Leave out (or send []) to check versions only"
    },
    "packages": {
      "type": "array",
      "minItems": 0,
      "maxItems": 50,
      "description": "Optional package URLs with versions, answered as POST /v1/osv/check answers them (advisories, fixed versions, KEV flag). Types: npm, pypi, golang, cargo, maven (nuget, gem and composer are answered as ecosystem_unavailable until imported)",
      "items": {
        "type": "string",
        "description": "purl with a version, e.g. pkg:npm/lodash@4.17.20 or pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
        "pattern": "^pkg:",
        "minLength": 7,
        "maxLength": 512
      }
    }
  },
  "required": [],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "summary": {
      "items": 2,
      "matched": 2,
      "unknown": 0,
      "by_status": {
        "supported": 0,
        "security-only": 1,
        "eol": 1,
        "unknown": 0
      },
      "cves": 2,
      "in_kev": 1,
      "packages": 1,
      "packages_affected": 1,
      "packages_in_kev": 0
    },
    "eol": {
      "count": 2,
      "matched": 2,
      "unknown_count": 0,
      "results": [
        {
          "index": 0,
          "product": "nodejs",
          "version": "18.19.0",
          "found": true,
          "resolved": "nodejs",
          "label": "Node.js",
          "match": {
            "cycle": "18",
            "eol": "2025-04-30",
            "latest": "18.20.8",
            "latest_date": "2025-03-27",
            "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#18.20.8",
            "lts": "2022-10-25",
            "release_date": "2022-04-19",
            "support_until": "2023-10-18",
            "status": "eol",
            "days_until_eol": -527,
            "is_lts": true
          }
        },
        {
          "index": 1,
          "product": "python",
          "version": "3.12.4",
          "found": true,
          "resolved": "python",
          "label": "Python",
          "match": {
            "cycle": "3.12",
            "eol": "2028-10-31",
            "latest": "3.12.15",
            "latest_date": "2026-10-01",
            "link": "https://www.python.org/downloads/release/python-31215/",
            "lts": false,
            "release_date": "2023-10-02",
            "support_until": "2025-04-02",
            "status": "security-only",
            "days_until_eol": 753,
            "is_lts": false
          }
        }
      ],
      "unknown": []
    },
    "kev": {
      "count": 2,
      "in_kev": 1,
      "catalog_version": "2026.10.10",
      "results": [
        {
          "cve": "CVE-1999-0001",
          "in_kev": false
        },
        {
          "cve": "CVE-2021-44228",
          "in_kev": true,
          "vendor": "Apache",
          "product": "Log4j2",
          "name": "Apache Log4j2 Remote Code Execution Vulnerability",
          "date_added": "2021-12-10",
          "due_date": "2021-12-24",
          "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
          "known_ransomware": true,
          "notes_urls": [
            "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
          ]
        }
      ]
    },
    "osv": {
      "count": 1,
      "summary": {
        "packages": 1,
        "affected": 1,
        "versions_unknown": 0,
        "no_known_advisories": 0,
        "unanswered": 0,
        "advisories": 1,
        "malicious": 0,
        "in_kev": 0
      },
      "kev_catalog_version": "2026.10.10",
      "results": [
        {
          "purl": "pkg:npm/lodash@4.17.20",
          "ecosystem": "npm",
          "name": "lodash",
          "version": "4.17.20",
          "status": "affected",
          "malicious": false,
          "kev": false,
          "advisories": [
            {
              "id": "GHSA-35jh-r3h4-6jhm",
              "kind": "vuln",
              "aliases": [
                "CVE-2021-23337"
              ],
              "summary": "Command Injection in lodash",
              "severity": [
                {
                  "type": "CVSS_V3",
                  "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"
                }
              ],
              "severity_label": "HIGH",
              "published": "2021-05-06T16:05:51Z",
              "modified": "2026-09-10T03:49:04Z",
              "match": "affected",
              "fixed": [
                "4.17.21"
              ],
              "kev": false,
              "kev_cves": [],
              "sources": [
                {
                  "id": "GHSA-35jh-r3h4-6jhm",
                  "prefix": "GHSA",
                  "name": "GitHub Advisory Database",
                  "licence": "CC-BY-4.0",
                  "licence_url": "https://creativecommons.org/licenses/by/4.0/",
                  "url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
                }
              ]
            }
          ]
        }
      ]
    }
  },
  "meta": {
    "dataset": "stack",
    "datasets": {
      "eol": {
        "dataset": "eol",
        "snapshot_id": "eol-20261009T060000Z-d61970d6",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://endoflife.date",
        "licence": "MIT",
        "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
        "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
        "signature": "WdWwHSWQ_G2JQhNUUOLK2-ybxeaGDIlCXACYglJPGZZTgRWpKgas13mXUMY_MLIvO-sf14hJj19OjOx-QgCXDA",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/eol/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "kev": {
        "dataset": "kev",
        "snapshot_id": "kev-20261009T060000Z-f4586cc5",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "licence": "CC0-1.0",
        "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
        "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
        "signature": "OyjcING3gmbUyak8Lzy_uGqeNUJJal_0BqsoIIJ-bkxa2tk3bu1MsZtwSZ8xiXZllLl7UcrsLvIZMcaxVEVdAg",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/kev/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "osv": {
        "dataset": "osv",
        "snapshot_id": "osv-2026-10-09T06:00:00.000Z",
        "as_of": "2026-10-09T06:00:00.000Z",
        "source": "https://osv.dev/",
        "attribution": "Vulnerability data from OSV.dev and the upstream databases listed, under their licences; joined and reformatted by Stack Facts (records are modified: fields trimmed and reshaped).",
        "licence_url": "/licences",
        "ecosystems": {
          "npm": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 5,
            "sha256": "18381a44c0ac8924b4de17ccb980bb385ae3015a788e0f40b57e4feb0bcd2f64"
          }
        },
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "age_hours": 1,
        "cadence_hours": 6,
        "stale_at": "2026-10-09T18:00:00.001Z",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      }
    },
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_osv_check

Check dependencies for known vulnerabilities: up to 50 package purls (npm, PyPI, Go, crates.io, Maven) against OSV.dev, with fixed versions and CISA KEV flags.

`POST /v1/osv/check`: $0.005 per call (5000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/osv/check' -H 'content-type: application/json' -d '{"packages":["pkg:npm/lodash@4.17.20","pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1"]}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "packages": {
      "type": "array",
      "minItems": 1,
      "maxItems": 50,
      "description": "Package URLs with versions, in order. Types: npm, pypi, golang, cargo, maven (nuget, gem and composer are answered as ecosystem_unavailable until imported)",
      "items": {
        "type": "string",
        "description": "purl with a version, e.g. pkg:npm/lodash@4.17.20 or pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
        "pattern": "^pkg:",
        "minLength": 7,
        "maxLength": 512
      }
    }
  },
  "required": [
    "packages"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "count": 2,
    "summary": {
      "packages": 2,
      "affected": 2,
      "versions_unknown": 0,
      "no_known_advisories": 0,
      "unanswered": 0,
      "advisories": 2,
      "malicious": 0,
      "in_kev": 1
    },
    "kev_catalog_version": "2026.10.10",
    "results": [
      {
        "purl": "pkg:npm/lodash@4.17.20",
        "ecosystem": "npm",
        "name": "lodash",
        "version": "4.17.20",
        "status": "affected",
        "malicious": false,
        "kev": false,
        "advisories": [
          {
            "id": "GHSA-35jh-r3h4-6jhm",
            "kind": "vuln",
            "aliases": [
              "CVE-2021-23337"
            ],
            "summary": "Command Injection in lodash",
            "severity": [
              {
                "type": "CVSS_V3",
                "score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H"
              }
            ],
            "severity_label": "HIGH",
            "published": "2021-05-06T16:05:51Z",
            "modified": "2026-09-10T03:49:04Z",
            "match": "affected",
            "fixed": [
              "4.17.21"
            ],
            "kev": false,
            "kev_cves": [],
            "sources": [
              {
                "id": "GHSA-35jh-r3h4-6jhm",
                "prefix": "GHSA",
                "name": "GitHub Advisory Database",
                "licence": "CC-BY-4.0",
                "licence_url": "https://creativecommons.org/licenses/by/4.0/",
                "url": "https://github.com/advisories/GHSA-35jh-r3h4-6jhm"
              }
            ]
          }
        ]
      },
      {
        "purl": "pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
        "ecosystem": "Maven",
        "name": "org.apache.logging.log4j:log4j-core",
        "version": "2.14.1",
        "status": "affected",
        "malicious": false,
        "kev": true,
        "advisories": [
          {
            "id": "GHSA-jfh8-c2jp-5v3q",
            "kind": "vuln",
            "aliases": [
              "CVE-2021-44228"
            ],
            "summary": "Remote code injection in Log4j",
            "severity": [],
            "severity_label": "CRITICAL",
            "published": "2021-12-10T00:40:56Z",
            "modified": "2025-10-22T19:37:02Z",
            "match": "affected",
            "fixed": [
              "2.3.1",
              "2.12.2",
              "2.15.0"
            ],
            "kev": true,
            "kev_cves": [
              "CVE-2021-44228"
            ],
            "sources": [
              {
                "id": "GHSA-jfh8-c2jp-5v3q",
                "prefix": "GHSA",
                "name": "GitHub Advisory Database",
                "licence": "CC-BY-4.0",
                "licence_url": "https://creativecommons.org/licenses/by/4.0/",
                "url": "https://github.com/advisories/GHSA-jfh8-c2jp-5v3q"
              }
            ]
          }
        ]
      }
    ]
  },
  "meta": {
    "dataset": "osv",
    "datasets": {
      "osv": {
        "dataset": "osv",
        "snapshot_id": "osv-2026-10-09T06:00:00.000Z",
        "as_of": "2026-10-09T06:00:00.000Z",
        "source": "https://osv.dev/",
        "attribution": "Vulnerability data from OSV.dev and the upstream databases listed, under their licences; joined and reformatted by Stack Facts (records are modified: fields trimmed and reshaped).",
        "licence_url": "/licences",
        "ecosystems": {
          "Maven": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 1,
            "sha256": "0a0af280cdda1e3364b88d8cc643de8379039f8500da31908180f6bc5b64ffb6"
          },
          "npm": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 5,
            "sha256": "18381a44c0ac8924b4de17ccb980bb385ae3015a788e0f40b57e4feb0bcd2f64"
          }
        },
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "age_hours": 1,
        "cadence_hours": 6,
        "stale_at": "2026-10-09T18:00:00.001Z",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "kev": {
        "dataset": "kev",
        "snapshot_id": "kev-20261009T060000Z-f4586cc5",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "licence": "CC0-1.0",
        "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
        "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
        "signature": "YcR1ldc55CB3kaodZXUB9yJbMoM2JaLe-SIfVjv12-CQzVv68d76yPy2fwY_nOvKht-6zMC8BLA7QgJIwJeSAw",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/kev/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      }
    },
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_preflight

Call before installing a package an LLM suggested: one npm, PyPI or purl version against OSV, CISA KEV and EOL data. Returns ok, warn or block with reasons.

`POST /v1/preflight`: $0.008 per call (8000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/preflight' -H 'content-type: application/json' -d '{"package":"pkg:npm/lodash@4.17.20"}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "package": {
      "type": "string",
      "description": "Package URL with a version, e.g. pkg:npm/lodash@4.17.20 or pkg:pypi/jinja2@2.11.2. Types: npm, pypi, golang, cargo, maven (nuget, gem and composer are refused until imported). Send this, or ecosystem, name and version",
      "pattern": "^pkg:",
      "minLength": 7,
      "maxLength": 512
    },
    "ecosystem": {
      "type": "string",
      "description": "npm, pypi, golang, cargo or maven (with name and version, instead of package)",
      "maxLength": 200
    },
    "name": {
      "type": "string",
      "description": "Package name as the registry spells it, e.g. lodash, @babel/traverse, jinja2, org.apache.logging.log4j:log4j-core",
      "maxLength": 200
    },
    "version": {
      "type": "string",
      "description": "Exact version, e.g. 4.17.20",
      "maxLength": 200
    }
  },
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "package": {
      "purl": "pkg:npm/lodash@4.17.20",
      "ecosystem": "npm",
      "name": "lodash",
      "version": "4.17.20"
    },
    "verdict": "warn",
    "reasons": [
      {
        "code": "vuln",
        "verdict": "warn",
        "id": "GHSA-35jh-r3h4-6jhm",
        "aliases": [
          "CVE-2021-23337"
        ],
        "summary": "Command Injection in lodash",
        "severity": "HIGH",
        "fixed": [
          "4.17.21"
        ]
      }
    ],
    "kev_catalog_version": "2026.10.10"
  },
  "meta": {
    "dataset": "preflight",
    "datasets": {
      "osv": {
        "dataset": "osv",
        "snapshot_id": "osv-2026-10-09T06:00:00.000Z",
        "as_of": "2026-10-09T06:00:00.000Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "age_hours": 1,
        "cadence_hours": 6,
        "stale_at": "2026-10-09T18:00:00.001Z",
        "stale": false,
        "source": "https://osv.dev/",
        "attribution": "Vulnerability data from OSV.dev and the upstream databases listed, under their licences; joined and reformatted by Stack Facts (records are modified: fields trimmed and reshaped).",
        "licence_url": "/licences",
        "ecosystems": {
          "npm": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 5,
            "sha256": "18381a44c0ac8924b4de17ccb980bb385ae3015a788e0f40b57e4feb0bcd2f64"
          }
        },
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "kev": {
        "dataset": "kev",
        "snapshot_id": "kev-20261009T060000Z-f4586cc5",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "licence": "CC0-1.0",
        "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
        "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
        "signature": "67rqguwNMKyxuF_Y6jf8kVcnYzlsrMEPoeb9YjElJFA4alwIuYpkNBV-WUGGVsSZZW4fa0-eV85NorA6ooKUCw",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/kev/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "eol": {
        "dataset": "eol",
        "snapshot_id": "eol-20261009T060000Z-d61970d6",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://endoflife.date",
        "licence": "MIT",
        "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
        "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
        "signature": "pBDE3WePUcxZVS6_mV30Hjky4sT0Zvdp8uayFdfDpg6vYb4ogv3NKLto2DAt3o1IE03BZ5p4TZRexDUPbZnlDw",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/eol/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      }
    },
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_cve_lookup

Get any CVE: its CISA KEV entry, if exploited, plus every OSV advisory aliased to it, with affected packages (purls), fixed versions and CVSS.

`GET /v1/cve/{id}`: $0.002 per call (2000 atomic USDC).

```sh
curl -si 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/cve/CVE-2021-44228'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "description": "CVE id, e.g. CVE-2021-44228",
      "pattern": "^[Cc][Vv][Ee]-\\d{4}-\\d{4,}$",
      "maxLength": 32
    }
  },
  "required": [
    "id"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "cve": "CVE-2021-44228",
    "in_kev": true,
    "kev": {
      "cve": "CVE-2021-44228",
      "in_kev": true,
      "vendor": "Apache",
      "product": "Log4j2",
      "name": "Apache Log4j2 Remote Code Execution Vulnerability",
      "date_added": "2021-12-10",
      "due_date": "2021-12-24",
      "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
      "known_ransomware": true,
      "notes_urls": [
        "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
      ]
    },
    "kev_catalog_version": "2026.10.10",
    "advisory_count": 1,
    "advisories": [
      {
        "id": "GHSA-jfh8-c2jp-5v3q",
        "kind": "vuln",
        "ecosystem": "Maven",
        "aliases": [
          "CVE-2021-44228"
        ],
        "summary": "Remote code injection in Log4j",
        "severity": [],
        "severity_label": "CRITICAL",
        "published": "2021-12-10T00:40:56Z",
        "modified": "2025-10-22T19:37:02Z",
        "affected": [
          {
            "name": "org.apache.logging.log4j:log4j-core",
            "purl": "pkg:maven/org.apache.logging.log4j/log4j-core",
            "fixed": [
              "2.3.1",
              "2.12.2",
              "2.15.0"
            ]
          }
        ],
        "sources": [
          {
            "id": "GHSA-jfh8-c2jp-5v3q",
            "prefix": "GHSA",
            "name": "GitHub Advisory Database",
            "licence": "CC-BY-4.0",
            "licence_url": "https://creativecommons.org/licenses/by/4.0/",
            "url": "https://github.com/advisories/GHSA-jfh8-c2jp-5v3q"
          }
        ]
      }
    ]
  },
  "meta": {
    "dataset": "cve",
    "datasets": {
      "osv": {
        "dataset": "osv",
        "snapshot_id": "osv-2026-10-09T06:00:00.000Z",
        "as_of": "2026-10-09T06:00:00.000Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "age_hours": 1,
        "cadence_hours": 6,
        "stale_at": "2026-10-09T18:00:00.001Z",
        "stale": false,
        "source": "https://osv.dev/",
        "attribution": "Vulnerability data from OSV.dev and the upstream databases listed, under their licences; joined and reformatted by Stack Facts (records are modified: fields trimmed and reshaped).",
        "licence_url": "/licences",
        "ecosystems": {
          "Go": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 2,
            "sha256": "4bdffa084bd959a140c3fe55baadf8369170722a96a9697d46acbc865fdc66b8"
          },
          "Maven": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 1,
            "sha256": "0a0af280cdda1e3364b88d8cc643de8379039f8500da31908180f6bc5b64ffb6"
          },
          "PyPI": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 3,
            "sha256": "7db510af7d8fe72483a0c3448925fd8dfb47a232401dd9ee8a79687301b31bc6"
          },
          "crates.io": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 2,
            "sha256": "33e36b56b0bedb89c0add1475da3b8c282c9a70b6c466b70d16bc7ad6cc29e6a"
          },
          "npm": {
            "imported_at": "2026-10-09T06:00:00.000Z",
            "checked_at": "2026-10-09T06:00:00.000Z",
            "records": 5,
            "sha256": "18381a44c0ac8924b4de17ccb980bb385ae3015a788e0f40b57e4feb0bcd2f64"
          }
        },
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "kev": {
        "dataset": "kev",
        "snapshot_id": "kev-20261009T060000Z-f4586cc5",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "licence": "CC0-1.0",
        "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
        "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
        "signature": "tCf3F6A3vq1mrM5ueQgMprTHaRF7uq4BT-gyJou0xLcbC-zcayD89CYV9K27Irw30YJ5CZhXMsaVnz2ES_4xAw",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/kev/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      }
    },
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_stack_watch

Check dependencies, runtimes and OS versions for what changed since your last call: EOL passed or near, new OSV advisories, new CISA KEV hits. Returns a token.

`POST /v1/stack/watch`: $0.012 per call (12000 atomic USDC).

```sh
curl -si -X POST 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/stack/watch' -H 'content-type: application/json' -d '{"items":["nodejs@18.19.0"],"cves":["CVE-2021-44228"],"warn_days":90}'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "description": "Send either the stack (items, cves, packages, warn_days: starts a watch) or a token from the last answer (token, full: a re-check), not both",
  "properties": {
    "items": {
      "type": "array",
      "minItems": 0,
      "maxItems": 50,
      "description": "The versions to check, in order: \"product@version\" strings or {product, version} objects. Needed unless packages is sent",
      "items": {
        "anyOf": [
          {
            "type": "string",
            "description": "product@version, e.g. nodejs@18.19.0 (GET /v1/eol/index lists products)",
            "pattern": "^[^@]+@.+$",
            "minLength": 3,
            "maxLength": 401
          },
          {
            "type": "object",
            "properties": {
              "product": {
                "type": "string",
                "description": "endoflife.date product id or alias, e.g. nodejs, python, ubuntu (GET /v1/eol/index lists them)",
                "minLength": 1,
                "maxLength": 200
              },
              "version": {
                "type": "string",
                "description": "Version string, e.g. 18.19.0 (send \"3.10\" as a string, not a number)",
                "minLength": 1,
                "maxLength": 200
              }
            },
            "required": [
              "product",
              "version"
            ],
            "additionalProperties": false
          }
        ]
      }
    },
    "cves": {
      "type": "array",
      "minItems": 0,
      "maxItems": 100,
      "items": {
        "type": "string",
        "description": "CVE id, e.g. CVE-2021-44228",
        "pattern": "^[Cc][Vv][Ee]-\\d{4}-\\d{4,}$",
        "maxLength": 32
      },
      "description": "Optional CVE ids to check against CISA KEV; duplicates are merged. Leave out (or send []) to check versions only"
    },
    "packages": {
      "type": "array",
      "minItems": 0,
      "maxItems": 50,
      "description": "Optional package URLs with versions, answered as POST /v1/osv/check answers them (advisories, fixed versions, KEV flag). Types: npm, pypi, golang, cargo, maven (nuget, gem and composer are answered as ecosystem_unavailable until imported)",
      "items": {
        "type": "string",
        "description": "purl with a version, e.g. pkg:npm/lodash@4.17.20 or pkg:maven/org.apache.logging.log4j/log4j-core@2.14.1",
        "pattern": "^pkg:",
        "minLength": 7,
        "maxLength": 512
      }
    },
    "warn_days": {
      "type": "integer",
      "minimum": 0,
      "maximum": 365,
      "description": "Start only: report an item as due soon when its EOL date is this many days away or fewer (default 90, 0 = off). Kept in the token"
    },
    "token": {
      "type": "string",
      "description": "Re-check only: data.watch.token from your last answer. Nothing else with it but full. It holds your stack in readable form; keep it as you would the stack",
      "pattern": "^sfw1\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]{43}$",
      "maxLength": 48000
    },
    "full": {
      "type": "boolean",
      "description": "Re-check only: also return data.check, the full current stack answer, at the same price (default false)"
    }
  },
  "required": [],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "watch": {
      "kind": "start",
      "token": "sfw1.eyJleHAiOiIyMDI3LTA0LTA3VDA3OjAwOjAwLjAwMFoiLCJpYXQiOiIyMDI2LTEwLTA5VDA3OjAwOjAwLjAwMFoiLCJzZWVuIjp7ImVvbCI6W3siYyI6IjE4IiwiZCI6ZmFsc2UsImUiOiIyMDI1LTA0LTMwIiwiZiI6dHJ1ZSwicyI6ImVvbCJ9XSwia2V2IjpbIkNWRS0yMDIxLTQ0MjI4Il0sIm9zdiI6W119LCJzbmFwIjp7ImVvbCI6ImVvbC0yMDI2MTAwOVQwNjAwMDBaLWQ2MTk3MGQ2Iiwia2V2Ijoia2V2LTIwMjYxMDA5VDA2MDAwMFotZjQ1ODZjYzUiLCJvc3YiOm51bGx9LCJzdGFjayI6eyJjdmVzIjpbIkNWRS0yMDIxLTQ0MjI4Il0sIml0ZW1zIjpbWyJub2RlanMiLCIxOC4xOS4wIl1dLCJwYWNrYWdlcyI6W119LCJ2IjoxLCJ3YXJuX2RheXMiOjkwfQ.4N2Rycs29lHafmTmjyUrsaPTvnSvl6SkaVV8t3FoGd0",
      "checked_at": "2026-10-09T07:00:00.000Z",
      "expires_at": "2027-04-07T07:00:00.000Z",
      "warn_days": 90,
      "stack": {
        "items": [
          "nodejs@18.19.0"
        ],
        "cves": [
          "CVE-2021-44228"
        ],
        "packages": []
      },
      "suggested_recheck_at": "2026-10-09T10:00:00.000Z",
      "next": "POST /v1/stack/watch {\"token\":\"<watch.token>\"} returns only what changed ($0.012)"
    },
    "check": {
      "summary": {
        "items": 1,
        "matched": 1,
        "unknown": 0,
        "by_status": {
          "supported": 0,
          "security-only": 0,
          "eol": 1,
          "unknown": 0
        },
        "cves": 1,
        "in_kev": 1
      },
      "eol": {
        "count": 1,
        "matched": 1,
        "unknown_count": 0,
        "results": [
          {
            "index": 0,
            "product": "nodejs",
            "version": "18.19.0",
            "found": true,
            "resolved": "nodejs",
            "label": "Node.js",
            "match": {
              "cycle": "18",
              "eol": "2025-04-30",
              "latest": "18.20.8",
              "latest_date": "2025-03-27",
              "link": "https://github.com/nodejs/node/blob/main/doc/changelogs/CHANGELOG_V18.md#18.20.8",
              "lts": "2022-10-25",
              "release_date": "2022-04-19",
              "support_until": "2023-10-18",
              "status": "eol",
              "days_until_eol": -527,
              "is_lts": true
            }
          }
        ],
        "unknown": []
      },
      "kev": {
        "count": 1,
        "in_kev": 1,
        "catalog_version": "2026.10.10",
        "results": [
          {
            "cve": "CVE-2021-44228",
            "in_kev": true,
            "vendor": "Apache",
            "product": "Log4j2",
            "name": "Apache Log4j2 Remote Code Execution Vulnerability",
            "date_added": "2021-12-10",
            "due_date": "2021-12-24",
            "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
            "known_ransomware": true,
            "notes_urls": [
              "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
            ]
          }
        ]
      }
    }
  },
  "meta": {
    "dataset": "stack",
    "datasets": {
      "eol": {
        "dataset": "eol",
        "snapshot_id": "eol-20261009T060000Z-d61970d6",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://endoflife.date",
        "licence": "MIT",
        "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
        "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
        "signature": "HZa2ZQYvoVydMgALXWhElZo5pGC2cSVCSQAbSsw_sPIbVMTzlaYwtox3x-eNGOqRpX3l4fXQBjea6Yh7reN6CA",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/eol/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      },
      "kev": {
        "dataset": "kev",
        "snapshot_id": "kev-20261009T060000Z-f4586cc5",
        "as_of": "2026-10-09T06:00:00Z",
        "retrieved_at": "2026-10-09T06:00:00.000Z",
        "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
        "licence": "CC0-1.0",
        "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
        "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
        "signature": "HbxWSMcHi65db2FpbS55iqUePmJ0l9jQMyBe9Eqkkr0BCM-u1ILMjOGJpCDCeZEjF7i3rp-LUYR3aUqEQddsAw",
        "key_id": "sf-test-1",
        "manifest_url": "/v1/kev/manifest",
        "stale": false,
        "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
      }
    },
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_preview

Preview a dataset for free: sample end-of-life products, the newest CISA KEV entries or LLM prices, signed like the paid answers.

`GET /v1/{dataset}/preview`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/preview'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "dataset": {
      "type": "string",
      "description": "Dataset: eol (software end-of-life), kev (CISA known exploited vulnerabilities) or models (LLM prices)",
      "enum": [
        "eol",
        "kev",
        "models"
      ]
    }
  },
  "required": [
    "dataset"
  ],
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "models": [
      {
        "id": "openai/gpt-5",
        "name": "GPT-5",
        "input_usd_per_mtok": 1.25,
        "output_usd_per_mtok": 10,
        "cache_read_usd_per_mtok": 0.125,
        "context_tokens": 400000,
        "deprecated": false
      },
      {
        "id": "openai/gpt-4o",
        "name": "GPT-4o",
        "input_usd_per_mtok": 2,
        "output_usd_per_mtok": 10,
        "cache_read_usd_per_mtok": 1.25,
        "context_tokens": 128000,
        "deprecated": false
      }
    ],
    "next": "GET /v1/models/{provider}/{model} returns every price, limit, capability and deprecation field (paid); GET /v1/try/models shows that shape for openai/gpt-4o (free)"
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_try

Try a paid answer for free on a fixed example: nodejs end-of-life cycles, CVE-2021-44228 in CISA KEV, or openai/gpt-4o prices.

`GET /v1/try/{dataset}`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/try/kev'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "dataset": {
      "type": "string",
      "description": "Dataset: eol (software end-of-life), kev (CISA known exploited vulnerabilities) or models (LLM prices)",
      "enum": [
        "eol",
        "kev",
        "models"
      ]
    }
  },
  "required": [
    "dataset"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "cve": "CVE-2021-44228",
    "in_kev": true,
    "vendor": "Apache",
    "product": "Log4j2",
    "name": "Apache Log4j2 Remote Code Execution Vulnerability",
    "date_added": "2021-12-10",
    "due_date": "2021-12-24",
    "required_action": "For all affected software assets for which updates exist, the only acceptable remediation actions are: 1) Apply updates; OR 2) remove affected assets from agency networks. Temporary mitigations using one of the measures provided at https://www.cisa.gov/uscert/ed-22-02-apache-log4j-recommended-mitigation-measures are only acceptable until updates are available.",
    "known_ransomware": true,
    "notes_urls": [
      "https://nvd.nist.gov/vuln/detail/CVE-2021-44228"
    ],
    "catalog_version": "2026.10.10"
  },
  "meta": {
    "dataset": "kev",
    "snapshot_id": "kev-20261009T060000Z-f4586cc5",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json",
    "licence": "CC0-1.0",
    "attribution": "Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.",
    "sha256": "f4586cc54bee30640f4364100eb70b7f25e4927f1d41d1ae92c0fea298508e74",
    "signature": "Tne7KlsSRbohZK45rjItkYBKFOd8sEdJNcaYd7c6TSr7AfZsT996F8pd-fqDeB67MijhEWw6aCgiuZl5TYk5AA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/kev/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_index

List the valid ids for free: end-of-life product names and aliases, or LLM provider/model ids, so a paid call never misses.

`GET /v1/{dataset}/index`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/index'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "dataset": {
      "type": "string",
      "description": "eol (product ids and aliases) or models (provider/model ids)",
      "enum": [
        "eol",
        "models"
      ]
    }
  },
  "required": [
    "dataset"
  ],
  "additionalProperties": false
}
```

Example answer (long arrays shortened):

```json
{
  "data": {
    "count": 36,
    "providers": [
      "anthropic",
      "cerebras",
      "cohere",
      "deepseek",
      "google",
      "groq",
      "mistral",
      "moonshotai",
      "openai",
      "xai"
    ],
    "models": [
      "anthropic/claude-haiku-4-5",
      "openai/gpt-4o"
    ]
  },
  "meta": {
    "dataset": "models",
    "snapshot_id": "models-20261009T060000Z-f99d55c9",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json",
    "licence": "MIT",
    "attribution": "Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.",
    "sha256": "f99d55c9aa420561de698d63d70d9163afe8e0571c9c1fb8d4b5dae33423351a",
    "signature": "AvlI5FXM8fD9oncZAr9velQseOBO9X7KTRSgeKGrdFtJwHrmWJhWAqGQ__I5kLf90y307OHgPecfzO7ALf1_DQ",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/models/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_validate

Validate an id for free before paying: whether a product, product/cycle or provider/model exists, or a CVE id is well formed.

`GET /v1/validate`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/validate?dataset=eol&id=nodejs'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "dataset": {
      "type": "string",
      "description": "Dataset: eol (software end-of-life), kev (CISA known exploited vulnerabilities) or models (LLM prices)",
      "enum": [
        "eol",
        "kev",
        "models"
      ]
    },
    "id": {
      "type": "string",
      "description": "Id to check: nodejs, nodejs/22, CVE-2021-44228 or openai/gpt-4o",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": [
    "dataset",
    "id"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "dataset": "eol",
    "id": "nodejs",
    "valid": true,
    "resolved": "nodejs",
    "checked": "exists"
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_manifest

Get the signed manifest of a dataset's latest snapshot for free: snapshot_id, sha256, record count, source, licence and signature.

`GET /v1/{dataset}/manifest`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/manifest'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {
    "dataset": {
      "type": "string",
      "description": "Dataset: eol (software end-of-life), kev (CISA known exploited vulnerabilities) or models (LLM prices)",
      "enum": [
        "eol",
        "kev",
        "models"
      ]
    }
  },
  "required": [
    "dataset"
  ],
  "additionalProperties": false
}
```

Example answer:

```json
{
  "data": {
    "dataset": "eol",
    "version": 1,
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "fetched_at": "2026-10-09T06:00:00Z",
    "source_url": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "upstream_etag": null,
    "record_count": 16,
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "prev_snapshot_id": null,
    "key_id": "sf-test-1",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA"
  },
  "meta": {
    "dataset": "eol",
    "snapshot_id": "eol-20261009T060000Z-d61970d6",
    "as_of": "2026-10-09T06:00:00Z",
    "retrieved_at": "2026-10-09T06:00:00.000Z",
    "source": "https://endoflife.date",
    "licence": "MIT",
    "attribution": "Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.",
    "sha256": "d61970d601c71df4855979790737437ee743889c55474b5406ef99be9d4de2ef",
    "signature": "8vBLWTyZaLEaHiY2z9B6rRGXZeZgvVHVHasvD89lLxOUHFbJXgX4aT5Aau38spDDXMwkoSl7rn7o-hAqemqmBA",
    "key_id": "sf-test-1",
    "manifest_url": "/v1/eol/manifest",
    "stale": false,
    "notice": "Normalised (modified) from the source. Provided as is, without warranty. Not endorsed by the source."
  }
}
```

### stackfacts_keys

Get the Ed25519 public keys (JWK) that verify the signature on every answer and snapshot manifest.

`GET /v1/keys`: free.

```sh
curl -s 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/keys'
```

Input (JSON Schema; path arguments fill the route, the rest go in the query for GET or the JSON body for POST):

```json
{
  "type": "object",
  "properties": {},
  "additionalProperties": false
}
```

Example answer:

```json
{
  "keys": [
    {
      "kty": "OKP",
      "crv": "Ed25519",
      "alg": "EdDSA",
      "use": "sig",
      "kid": "sf-test-1",
      "x": "dVm4zPfjsbjc4NUFEW_nssyz8u_IN9Vy5frOhJyWoOY"
    }
  ]
}
```

## Example files

### curl.md

[https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/curl.md](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/curl.md): curl: a free call, then a paid route's 402 and its PAYMENT-REQUIRED header decoded.

````markdown
# Stack Facts with curl: a free try, then a 402 decoded

Every paid route of Stack Facts first answers `402 Payment Required` with the price in a `PAYMENT-REQUIRED`
header (x402 v2). You need `curl`, `base64` and `python3` (for pretty JSON). No account or API key.
Dates and the `payTo` address in the sample answers will differ from yours.

Set the base URL once:

```sh
BASE=https://g5joilnmeep4ntz3nwezxt5dmu.srv.us
```

## 1. Free: a signed sample answer

`/v1/try/eol` is free and has the same shape as the paid `/v1/eol/{product}` (it always answers for nodejs).

```sh
curl -s "$BASE/v1/try/eol"
```

```text
{"data":{"product":"nodejs","label":"Node.js","cycles":[{"cycle":"26","eol":"2029-04-30","latest":"26.11.1", …
```

Every answer is `{data, meta}`; `meta` names the snapshot, its sha256 and an Ed25519 signature you can check against
`/v1/keys`.

## 2. Paid: the 402 offer

The same call on the paid route, with `-i` to see the status line and headers. Errors and 402s are never charged.

```sh
curl -si "$BASE/v1/eol/nodejs" | grep -iE '^(HTTP/|payment-required:|cache-control:)' | cut -c1-80
```

```text
HTTP/1.1 402 Payment Required
cache-control: no-store
payment-required: eyJ4NDAyVmVyc2lvbiI6MiwiZXJyb3IiOiJQQVlNRU5ULVNJR05BVFVSRSBoZW
```

## 3. Decode the offer

`PAYMENT-REQUIRED` is standard base64 of UTF-8 JSON (the 402 body holds the same JSON, so
`curl -s "$BASE/v1/eol/nodejs"` shows it too).

```sh
curl -s -o /dev/null -D - "$BASE/v1/eol/nodejs" | grep -i '^payment-required:' | cut -d' ' -f2 | tr -d '\r' | base64 --decode | python3 -m json.tool --no-ensure-ascii
```

The output, with `extensions` cut (it holds `bazaar`, the route's input and output schema with an example answer,
and `payment-identifier`, for safe retries):

```json
{
  "x402Version": 2,
  "error": "PAYMENT-SIGNATURE header is required",
  "resource": {
    "url": "https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs",
    "description": "List every release cycle of a product (Node.js, Python, Ubuntu…) and if it is still supported: EOL (end-of-life) date, support status, LTS, latest version.",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "1000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0x1111111111111111111111111111111111111111",
      "maxTimeoutSeconds": 60,
      "extra": {
        "name": "USD Coin",
        "version": "2"
      }
    }
  ]
}
```

Read it as: pay `amount` = 1000 atomic units of USDC (6 decimals, so $0.001) at `asset` (USDC on the chain named in
`network`) to `payTo`, using the `exact` scheme (an EIP-3009 transferWithAuthorization signed with the
EIP-712 domain `extra.name` / `extra.version`), within `maxTimeoutSeconds`.

## 4. Pay

Paying means signing that authorization with a wallet, so it is not a curl one-liner. Build the PaymentPayload
(`x402Version`, `resource`, `accepted` = the accepts entry, `payload` = `{signature, authorization}`), base64 its
JSON and call again:

```text
curl -s -H "PAYMENT-SIGNATURE: <base64 PaymentPayload>" "$BASE/v1/eol/nodejs"
```

The answer is a 200 with the data and a `PAYMENT-RESPONSE` header (base64 JSON with the settlement `transaction`).
Two complete clients do this step: `fetch-x402.mjs` (Node, no dependencies) and `python_x402.py` (Python standard
library), next to this file. Add your signer to pay. The stock
x402 client for JavaScript is `@x402/fetch`.
````

### fetch-x402.mjs

[https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/fetch-x402.mjs](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/fetch-x402.mjs): Node 18+, no dependencies: an x402 client that pays one call (add a viem account as the signer).

```js
// Stack Facts: pay for one call with x402, no dependencies (Node 18+).
//   node fetch-x402.mjs [url] [json-body]
// Default url: https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs (GET); a json-body makes it a POST.
// Env: MAX_ATOMIC = the most this script will pay for one call, in atomic USDC (6 decimals; default 10000 = $0.01).
//
// The flow (x402 v2, scheme "exact"): call the route -> 402 with the offer in the PAYMENT-REQUIRED header (base64
// JSON) -> sign an EIP-3009 TransferWithAuthorization for exactly that offer -> call again with PAYMENT-SIGNATURE
// (base64 JSON) -> 200 with the answer and PAYMENT-RESPONSE (base64 JSON: the settlement transaction).
//
// The default signer is a placeholder that cannot pay. To pay, pass a signer shaped like a viem account
// ({address, signTypedData}), e.g. privateKeyToAccount(key) from 'viem/accounts' for a wallet holding USDC.
// Alternative: the @x402/fetch package (wrapFetchWithPayment with @x402/evm's ExactEvmScheme).

import { realpathSync } from 'node:fs';
import { pathToFileURL } from 'node:url';

const DEFAULT_URL = 'https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs';
const CHAIN_IDS = { 'eip155:84532': 84532, 'eip155:8453': 8453 }; // Base testnet, Base

// Standard base64 of UTF-8 JSON, both ways (the header encoding).
const encode = (value) => Buffer.from(JSON.stringify(value), 'utf8').toString('base64');
const decode = (b64) => JSON.parse(Buffer.from(String(b64).trim(), 'base64').toString('utf8'));

// Placeholder signer: a fixed address and a dummy 65-byte signature. Replace it to pay.
export const mockSigner = {
  address: '0x5555555555555555555555555555555555555555',
  async signTypedData(_typedData) {
    return '0x' + 'cd'.repeat(65);
  },
};

// The EIP-712 typed data a real wallet signs for one "exact" offer (EIP-3009 transferWithAuthorization on USDC).
export function transferAuthorization(accepted, from, { now = Math.floor(Date.now() / 1000) } = {}) {
  const chainId = CHAIN_IDS[accepted.network];
  if (!chainId) throw new Error(`unknown network ${accepted.network}`);
  const nonce = '0x' + Array.from(crypto.getRandomValues(new Uint8Array(32)), (b) => b.toString(16).padStart(2, '0')).join('');
  const message = {
    from,
    to: accepted.payTo,
    value: accepted.amount,
    validAfter: '0',
    validBefore: String(now + accepted.maxTimeoutSeconds),
    nonce,
  };
  return {
    domain: { name: accepted.extra.name, version: accepted.extra.version, chainId, verifyingContract: accepted.asset },
    types: {
      TransferWithAuthorization: [
        { name: 'from', type: 'address' },
        { name: 'to', type: 'address' },
        { name: 'value', type: 'uint256' },
        { name: 'validAfter', type: 'uint256' },
        { name: 'validBefore', type: 'uint256' },
        { name: 'nonce', type: 'bytes32' },
      ],
    },
    primaryType: 'TransferWithAuthorization',
    message,
  };
}

// fetch, plus paying one 402 offer of at most maxAtomic. -> {response, offer, accepted, paid}: offer = the decoded
// PAYMENT-REQUIRED (null if the first call was not a 402), accepted = the entry paid, paid = the decoded
// PAYMENT-RESPONSE (null if none).
export async function payingFetch(url, init = {}, { signer = mockSigner, maxAtomic = 10000n, fetchImpl = fetch } = {}) {
  const first = await fetchImpl(url, init);
  if (first.status !== 402) return { response: first, offer: null, accepted: null, paid: null };
  const offer = decode(first.headers.get('payment-required'));
  await first.arrayBuffer(); // the body is the same JSON as the header
  const accepted = offer.accepts.find((a) => a.scheme === 'exact' && CHAIN_IDS[a.network]);
  if (!accepted) throw new Error('no offer this client can pay (needs scheme exact on Base or its testnet)');
  if (BigInt(accepted.amount) > BigInt(maxAtomic)) {
    throw new Error(`price ${accepted.amount} atomic USDC is over the limit of ${maxAtomic}; not paying`);
  }
  const typedData = transferAuthorization(accepted, signer.address);
  const payment = {
    x402Version: offer.x402Version,
    resource: offer.resource,
    accepted,
    payload: { signature: await signer.signTypedData(typedData), authorization: typedData.message },
  };
  const headers = new Headers(init.headers);
  headers.set('PAYMENT-SIGNATURE', encode(payment));
  const response = await fetchImpl(url, { ...init, headers });
  const settled = response.headers.get('payment-response');
  return { response, offer, accepted, paid: settled ? decode(settled) : null };
}

// CLI: pays one call and prints one JSON object: {status, price, paid, body}. (realpath: the script may sit behind a symlink.)
if (process.argv[1] && import.meta.url === pathToFileURL(realpathSync(process.argv[1])).href) {
  const [url = DEFAULT_URL, body] = process.argv.slice(2);
  const init = body ? { method: 'POST', headers: { 'content-type': 'application/json' }, body } : {};
  try {
    const { response, accepted, paid } = await payingFetch(url, init, { maxAtomic: BigInt(process.env.MAX_ATOMIC || 10000) });
    const text = await response.text();
    let json = null;
    try { json = JSON.parse(text); } catch { json = text; }
    const price = accepted ? { amount: accepted.amount, asset: accepted.asset, network: accepted.network, payTo: accepted.payTo } : null;
    console.log(JSON.stringify({ status: response.status, price, paid, body: json }, null, 2));
    process.exitCode = response.ok ? 0 : 1;
  } catch (err) {
    console.error(`fetch-x402: ${err.message}`);
    process.exitCode = 2;
  }
}
```

### python_x402.py

[https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/python_x402.py](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/examples/python_x402.py): Python 3 standard library: the same client (add an EIP-712 signer).

```python
"""Stack Facts: pay for one call with x402 from Python, standard library only (Python 3.9+).

    python3 python_x402.py [url] [json-body]

Default url: https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs (GET); a json-body makes it a POST.
Env: MAX_ATOMIC = the most this script will pay for one call, in atomic USDC (6 decimals; default 10000 = $0.01).

The flow (x402 v2, scheme "exact"): call the route -> 402 with the offer in the PAYMENT-REQUIRED header (base64
JSON) -> sign an EIP-3009 TransferWithAuthorization for exactly that offer -> call again with PAYMENT-SIGNATURE
(base64 JSON) -> 200 with the answer and PAYMENT-RESPONSE (base64 JSON: the settlement transaction).

The default signer is a placeholder that cannot pay. To pay, replace mock_sign with an EIP-712 signer for the
typed data built below (for example eth_account's Account.sign_typed_data with a key holding USDC), and
MOCK_ADDRESS with that key's address.
"""
import base64
import json
import os
import secrets
import sys
import time
import urllib.error
import urllib.request

DEFAULT_URL = "https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/nodejs"
CHAIN_IDS = {"eip155:84532": 84532, "eip155:8453": 8453}  # Base testnet, Base
MOCK_ADDRESS = "0x5555555555555555555555555555555555555555"


def encode(value):
    """Standard base64 of UTF-8 JSON (the header encoding)."""
    return base64.b64encode(json.dumps(value, separators=(",", ":")).encode("utf-8")).decode("ascii")


def decode(b64):
    return json.loads(base64.b64decode(b64.strip()).decode("utf-8"))


def mock_sign(typed_data):
    """Placeholder signer: a dummy 65-byte signature. Replace it to pay."""
    return "0x" + "cd" * 65


def transfer_authorization(accepted, sender, now=None):
    """The EIP-712 typed data a real wallet signs for one "exact" offer (EIP-3009 on USDC)."""
    chain_id = CHAIN_IDS.get(accepted["network"])
    if chain_id is None:
        raise ValueError("unknown network %s" % accepted["network"])
    now = int(time.time()) if now is None else now
    message = {
        "from": sender,
        "to": accepted["payTo"],
        "value": accepted["amount"],
        "validAfter": "0",
        "validBefore": str(now + accepted["maxTimeoutSeconds"]),
        "nonce": "0x" + secrets.token_hex(32),
    }
    return {
        "domain": {"name": accepted["extra"]["name"], "version": accepted["extra"]["version"],
                   "chainId": chain_id, "verifyingContract": accepted["asset"]},
        "types": {"TransferWithAuthorization": [
            {"name": "from", "type": "address"}, {"name": "to", "type": "address"},
            {"name": "value", "type": "uint256"}, {"name": "validAfter", "type": "uint256"},
            {"name": "validBefore", "type": "uint256"}, {"name": "nonce", "type": "bytes32"},
        ]},
        "primaryType": "TransferWithAuthorization",
        "message": message,
    }


def call(url, body=None, headers=None):
    """-> (status, headers, body bytes). A 4xx/5xx is an answer here, not an exception."""
    headers = dict(headers or {})
    data = None
    if body is not None:
        data = body.encode("utf-8")
        headers["content-type"] = "application/json"
    req = urllib.request.Request(url, data=data, headers=headers, method="POST" if data is not None else "GET")
    try:
        with urllib.request.urlopen(req, timeout=30) as res:
            return res.status, res.headers, res.read()
    except urllib.error.HTTPError as err:
        return err.code, err.headers, err.read()


def paying_call(url, body=None, max_atomic=10000, sign=mock_sign, sender=MOCK_ADDRESS):
    """Calls url, pays one 402 offer of at most max_atomic. -> (status, body bytes, accepted, settlement)."""
    status, headers, raw = call(url, body)
    if status != 402:
        return status, raw, None, None
    offer = decode(headers["PAYMENT-REQUIRED"])
    accepted = next((a for a in offer["accepts"] if a["scheme"] == "exact" and a["network"] in CHAIN_IDS), None)
    if accepted is None:
        raise RuntimeError("no offer this client can pay (needs scheme exact on Base or its testnet)")
    if int(accepted["amount"]) > int(max_atomic):
        raise RuntimeError("price %s atomic USDC is over the limit of %s; not paying" % (accepted["amount"], max_atomic))
    typed = transfer_authorization(accepted, sender)
    payment = {
        "x402Version": offer["x402Version"],
        "resource": offer["resource"],
        "accepted": accepted,
        "payload": {"signature": sign(typed), "authorization": typed["message"]},
    }
    status, headers, raw = call(url, body, {"PAYMENT-SIGNATURE": encode(payment)})
    settled = headers.get("PAYMENT-RESPONSE")
    return status, raw, accepted, decode(settled) if settled else None


def main(argv):
    url = argv[1] if len(argv) > 1 else DEFAULT_URL
    body = argv[2] if len(argv) > 2 else None
    try:
        status, raw, accepted, paid = paying_call(url, body, int(os.environ.get("MAX_ATOMIC", "10000")))
    except (RuntimeError, ValueError, KeyError, urllib.error.URLError) as err:
        print("python_x402: %s" % err, file=sys.stderr)
        return 2
    try:
        answer = json.loads(raw.decode("utf-8"))
    except ValueError:
        answer = raw.decode("utf-8", "replace")
    price = None
    if accepted:
        price = {k: accepted[k] for k in ("amount", "asset", "network", "payTo")}
    print(json.dumps({"status": status, "price": price, "paid": paid, "body": answer}, indent=2))
    return 0 if 200 <= status < 300 else 1


if __name__ == "__main__":
    sys.exit(main(sys.argv))
```

## Errors

Each code with its anchor: [https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors).

Every error is JSON: `{"error":{"code","message","next","docs"}}`, where `docs` links here (`https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#<code>`). Errors are never charged: a 4xx or 5xx is never settled.

| Code | HTTP | What it means | What to do |
|---|---|---|---|
| [payment_required](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_required) | 402 | The route is paid and the request carries no PAYMENT-SIGNATURE header. The body and the PAYMENT-REQUIRED header (base64) hold the x402 offer; it is sent before the input is read, on GET and POST. | Pay accepts[0] with an x402 v2 client and retry the same request with PAYMENT-SIGNATURE. |
| [payment_invalid](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_invalid) | 402 | PAYMENT-SIGNATURE could not be decoded, or the payment is not x402 v2 exact, or its signature is refused. | Build a new payment from the 402 offer. |
| [payment_replayed](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_replayed) | 402 | This payment (its nonce) was already used. | Sign a new payment. To get a paid answer again, send the same payment-identifier with the same payment. |
| [payment_underpaid](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_underpaid) | 402 | The payment amount is below the route price. | Pay at least accepts[0].amount (atomic USDC, 6 decimals). |
| [payment_expired](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_expired) | 402 | The payment is outside its time window: validBefore is less than 5 seconds away or past, or validAfter is still ahead. | Sign a new payment with a current time window. |
| [payment_wrong_network](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_wrong_network) | 402 | The payment is not on Base, network eip155:8453. | Pay on the network named in accepts[0].network. |
| [payment_wrong_asset](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_wrong_asset) | 402 | The payment is not in USDC at 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913. | Pay in the asset named in accepts[0].asset. |
| [payment_wrong_payto](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_wrong_payto) | 402 | The payment is to another address than ours. | Pay to accepts[0].payTo. |
| [payment_wrong_resource](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_wrong_resource) | 402 | The payment names another URL than the one called (or none). | Sign the payment for resource.url of this 402. |
| [facilitator_unavailable](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#facilitator_unavailable) | 503 | The x402 facilitator could not verify or settle the payment. Nothing was charged. | Retry later with a new payment. |
| [settlement_unknown](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#settlement_unknown) | 503 | Settlement did not confirm and may still have completed. The payment is held for reconciliation. | Retry with the SAME PAYMENT-SIGNATURE (never a new one); it is never charged twice. Retry within about 20 s (the 60 s signing window less the 10 s settle timeout and the 30 s Retry-After); after that the retry is refused as expired and the payment stays held for reconciliation. |
| [payment_in_progress](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_in_progress) | 409 | A request with the same payment-identifier is still being processed. | Retry in a few seconds with the same request. |
| [rate_limited](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#rate_limited) | 429 | Too many refused payments from this payer address or client IP (5 in 10 minutes). Not charged. | Wait for the Retry-After header, then send a correctly signed payment. |
| [bad_request](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#bad_request) | 400 | The input is not valid (see message). | Check the input schema in /openapi.json; GET /v1/validate checks an id for free. |
| [unknown_id](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#unknown_id) | 404 | The id is not in the dataset (for a batch: none of the items is). Not charged. For an unknown product on GET /v1/eol/{product} and /v1/eol/{product}/{cycle}, error.suggestions lists up to 5 closest product ids (empty if none is close). On GET /v1/eol/image: no part of the image (repository, runtime or base OS in the tag) matched a product and cycle. | Retry with a suggested id; GET /v1/eol/index or /v1/models/index lists valid ids (free). |
| [too_many_items](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#too_many_items) | 413 | The batch is over the limit: 50 items for /v1/eol/check, 100 CVEs for /v1/kev/check, 50 items, 50 packages and 100 CVEs for /v1/stack/check and /v1/stack/watch, 50 purls for /v1/osv/check; on /v1/stack/watch also a watch token over 32,000 characters at the start or 48,000 on a re-check. Not charged. | Split the batch (a watch: start two smaller watches). |
| [snapshot_expired](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#snapshot_expired) | 410 | The since snapshot_id is unknown or no longer kept, so no change list can be built. Not charged. | Download the full snapshot (/v1/{dataset}/snapshot) and use its snapshot_id as the next since. |
| [paused](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#paused) | 503 | Paid routes are temporarily paused. | Retry after the Retry-After header; free routes still answer. |
| [stale_data](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#stale_data) | 503 | The dataset was not refreshed within twice its cadence, so paid routes do not answer. | GET /health shows each dataset; retry later. |
| [payer_blocked](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payer_blocked) | 403 | The paying address is blocked by sanctions screening. Not settled. | None. |
| [l402_invalid](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#l402_invalid) | 401 | The Authorization: L402 credential failed a check (macaroon signature, a caveat, expiry or preimage) or its preimage was already used. Only sent when the L402 rail is on. | Pay the invoice in the WWW-Authenticate header and retry with the new macaroon and preimage. |
| [internal](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#internal) | 500 | An unexpected server error. Not charged. | Retry later. |
| [dataset_unavailable](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#dataset_unavailable) | 503 | No snapshot of the dataset is loaded yet. | GET /health shows each dataset; retry later. |
| [payments_unconfigured](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payments_unconfigured) | 503 | Payments are not available on this server. | Use the free routes; retry later. |
| [payment_id_conflict](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#payment_id_conflict) | 409 | The payment-identifier id was already used with a different payment or request. | Use a new payment-identifier id for a new payment. |
| [no_match](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#no_match) | 404 | No record meets the search (POST /v1/models/cheapest). Not charged. | Loosen the constraints. |
| [not_implemented](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#not_implemented) | 501 | This priced route is not available yet. A payment for it is never settled. | Use another route. |
| [token_invalid](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#token_invalid) | 400 | The watch token for POST /v1/stack/watch is not one we issued (its shape, signature or contents are wrong). Not charged. | Start a new watch: POST /v1/stack/watch with the stack. |
| [token_expired](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#token_expired) | 410 | The watch token for POST /v1/stack/watch is past its expires_at (180 days), or in a format no longer read. Not charged. | Start a new watch: POST /v1/stack/watch with the stack (it is in watch.stack of your last answer). |
| [watch_unavailable](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#watch_unavailable) | 503 | Stack watch is not available on this server. Not charged. | Use POST /v1/stack/check (no token). |
| [not_found](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#not_found) | 404 | No route at this path. | GET /llms.txt lists every route (free). |
| [method_not_allowed](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#method_not_allowed) | 405 | The path exists but not for this method. | Use a method from the Allow header. |
| [body_too_large](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#body_too_large) | 413 | The request body is over 64 KB. | Send a smaller body (split a batch). |
| [headers_too_large](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/docs/errors#headers_too_large) | 431 | The request headers are too large. | Send smaller headers. |

## Provenance

- Each source is fetched, normalised to one record per id and checked against the schema.
- A parse or schema failure, or fewer than 90 % of the previous records, keeps the previous snapshot live.
- Each snapshot is canonical JSON (RFC 8785) with a sha256 and an Ed25519-signed manifest (snapshot_id, sha256, record count, source, licence).
- The last 30 snapshots and their diffs are kept for the change feeds.

Verify a snapshot: sha256 of the canonical JSON (RFC 8785) of data.snapshot equals data.manifest.sha256; data.manifest.signature is Ed25519 (base64url) over the canonical JSON of the manifest without "signature", by the key in GET /v1/keys with that key_id.

### Software end-of-life dates

- Source: endoflife.date, [https://endoflife.date/api/v1/products/full](https://endoflife.date/api/v1/products/full), MIT. Attribution: Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.
- Refresh: every 24 h; stale (paid routes answer 503) after 48 h without a successful refresh.
- Snapshot: `eol-20261011T184823Z-fb3e9d0a`, as of 2026-10-11T18:48:23Z, 483 records, sha256 `fb3e9d0aeca0c7df060e0eaf2193f1cf74910961adc62857bde67de6167dc061`.
- Live: [/v1/eol/manifest](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/eol/manifest) (free) and [/health](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/health).

### Actively exploited CVEs (CISA KEV)

- Source: CISA KEV, [https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json](https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json), CC0-1.0. Attribution: Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.
- Refresh: every 4 h; stale (paid routes answer 503) after 8 h without a successful refresh.
- Snapshot: `kev-20261009T183031Z-bc476d6c`, as of 2026-10-09T18:30:31Z, 1739 records, sha256 `bc476d6c11a365d2f4371f6775a025d2d0e3d74bed5bca521838007452b956b0`.
- Live: [/v1/kev/manifest](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/kev/manifest) (free) and [/health](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/health).

### LLM prices, context windows and deprecation dates

- Source: LiteLLM, [https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json](https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json), MIT. Attribution: Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages.
- Source: models.dev, [https://models.dev/api.json](https://models.dev/api.json), MIT. Attribution: Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.
- Refresh: every 12 h; stale (paid routes answer 503) after 24 h without a successful refresh.
- Snapshot: `models-20261010T070852Z-7d710157`, as of 2026-10-10T07:08:52Z, 368 records, sha256 `7d710157205630955bc076be6979f3482143ce31046fb2042ed2917ef2d2a998`.
- Live: [/v1/models/manifest](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/v1/models/manifest) (free) and [/health](https://g5joilnmeep4ntz3nwezxt5dmu.srv.us/health).

## Licences

- Software end-of-life dates (MIT): Data from endoflife.date (https://endoflife.date), Copyright 2020 endoflife.date contributors, MIT License.
- Actively exploited CVEs (CISA KEV) (CC0-1.0): Source: CISA Known Exploited Vulnerabilities Catalog (CC0 1.0). Not endorsed by CISA or DHS.
- LLM prices, context windows and deprecation dates (MIT): Pricing data derived from LiteLLM, Copyright (c) 2023 Berri AI, MIT License. Community-maintained; may lag vendor pages. Data from models.dev (https://models.dev), Copyright (c) 2025 models.dev, MIT License.

Data is provided as is, without warranty of any kind. Payments are final. Every dataset is normalised (modified) from the sources named here, and no source endorses this service.

## Changelog

### 2026-10-12: 0.5.0

- x402 payments are now on Base mainnet (eip155:8453) in USDC (0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913).

### 2026-10-10: 0.4.0

- New paid route POST /v1/stack/watch ($0.012, all rails): start with a stack as for POST /v1/stack/check and get the full check plus a signed watch token; send the token back later to get only what changed since then (an end-of-life date passed or near, a CVE newly in CISA KEV, a new OSV advisory), the worst severity and a new token. No watch state is stored: the token holds the stack in readable form. MCP tool stackfacts_stack_watch.
- New error codes token_invalid (400), token_expired (410) and watch_unavailable (503) for POST /v1/stack/watch, all uncharged. too_many_items also covers a watch token over 32,000 characters at the start or 48,000 on a re-check.

### 2026-10-10: 0.3.0

- New paid route GET /v1/eol/image?image=node:18-alpine3.19 ($0.005, all rails): a container image tag read for the runtime, Java and base OS it names, each with its end-of-life cycle and status, and the worst of them.
- New field complete on GET /v1/eol/image: true when every part of the image matched a product and cycle. The headline is unknown when the image's own product did not match.

### 2026-10-10: 0.2.0

- New paid route POST /v1/osv/check ($0.005): up to 50 open-source packages as purls (npm, PyPI, Maven, Go, crates.io, NuGet, RubyGems, Packagist), answered with the OSV.dev advisories that match each version, their aliases, fixed versions and a kev flag when a CVE alias is in the CISA KEV catalog. MCP tool stackfacts_osv_check.
- OSV source licences (CC-BY 4.0, Apache-2.0, CC0) and attributions added to /licences.

### 2026-10-10: 0.1.0

- First version, on the Base test network (eip155:84532).
- Three datasets: software end-of-life dates (endoflife.date), actively exploited CVEs (CISA KEV), and LLM prices and limits (LiteLLM and models.dev), refreshed every 24, 4 and 12 hours.
- 14 paid tools on 15 priced routes over x402 v2 exact, including POST /v1/stack/check; free preview, try, index, validate and manifest routes; every answer signed with Ed25519.
- MCP at POST /mcp. Discovery: llms.txt, llms-full.txt, openapi.json, /.well-known/x402, /.well-known/api-catalog, the MCP server card, pricing.json, robots.txt and sitemap.xml.
- Free docs pages from the live data, each with a markdown twin: /docs/eol/<product>, /docs/kev-recent and /docs/models-changes.
- Examples: /examples/curl.md, /examples/fetch-x402.mjs (Node) and /examples/python_x402.py (Python).
