API reference

ThreatFilter exposes a small JSON API at https://api.threatfilter.dev. Every endpoint is unauthenticated, free, CORS-enabled, and cached for a few minutes at the Cloudflare edge. Rate-limit: be reasonable — there is no key and no per-IP enforcement today.

Tags returned in vendors, severity_label, exploitation_status, and sectors come from the upstream feeds. The dedicated ML classifier is currently unfunded, so most items ship with vendors: [] and these are filled in heuristically in the browser (and at /feed.xml) using the keyword catalog in src/lib/classify.ts.

GET /health

Health check

Lightweight liveness probe. Returns 200 with a constant payload — useful for uptime monitors.

Example request

curl https://api.threatfilter.dev/health

Example response

{
  "ok": true,
  "service": "threatfilter-api"
}

GET /sources

Source catalog + last fetch status

List every ingested feed source with its kind (rss/json/atom), category, last fetched_at, and last status.

Example request

curl https://api.threatfilter.dev/sources

Example response

{
  "sources": [
    {
      "id": "cisa-kev",
      "name": "CISA Known Exploited Vulnerabilities",
      "kind": "json",
      "category": "government",
      "enabled": true,
      "last_fetched_at": "2026-05-20T11:02:14Z",
      "last_status": "ok"
    },
    {
      "id": "nvd-recent",
      "name": "NVD Recent CVEs",
      "kind": "json",
      "category": "government",
      "enabled": true,
      "last_fetched_at": "2026-05-20T11:00:08Z",
      "last_status": "ok"
    }
  ]
}

GET /freshness

Most-recent ingest timestamp

Single timestamp of the most recently fetched item across all sources. Drives the freshness dot in the header.

Example request

curl https://api.threatfilter.dev/freshness

Example response

{
  "latest": "2026-05-20T11:02:14Z",
  "source_id": "cisa-kev"
}

GET /vendors

Vendor catalog + counts

Static vendor catalog (slug, label, category) annotated with the count of items currently linked to each vendor (when the materialized vendor_counts table is populated).

Example request

curl https://api.threatfilter.dev/vendors

Example response

{
  "vendors": [
    {
      "slug": "cisco",
      "label": "Cisco",
      "category": "Networking",
      "count": 184
    },
    {
      "slug": "fortinet",
      "label": "Fortinet",
      "category": "Networking",
      "count": 92
    },
    {
      "slug": "microsoft",
      "label": "Microsoft",
      "category": "OS",
      "count": 311
    }
  ]
}

GET /items

Paginated advisory feed

Keyset-paginated advisory feed ordered by published_at DESC, id DESC. Cursor is returned in next_cursor — feed it back as ?cursor= to get the next page. All filter params combine with AND.

Parameters

Name Type Description Example
limit int Page size (default 50, max 100). 50
cursor string Opaque cursor from a previous response's next_cursor field.
vendors csv Comma-separated vendor slugs. Items matching any of these vendors are returned (OR). cisco,fortinet
severity csv One or more of critical, high, medium, low, info (OR). critical,high
exploitation csv exploited | poc_available | unproven (OR). exploited
geo csv ISO-3166 alpha-2 country codes (OR). US,EU
sec csv Sector slugs (OR). Same vocabulary as /sector/<slug> pages. government,healthcare
q string Full-text search query (SQLite FTS5). Plain whitespace-separated terms; AND-combined. cve-2024

Example request

curl https://api.threatfilter.dev/items?limit=2&severity=critical

Example response

{
  "items": [
    {
      "id": 482301,
      "source_id": "cisa-kev",
      "title": "CISA adds CVE-2026-12345 to Known Exploited Vulnerabilities",
      "url": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
      "published_at": "2026-05-20T10:45:00Z",
      "fetched_at": "2026-05-20T11:02:14Z",
      "raw_summary": "A critical authentication bypass in a popular VPN appliance is being actively exploited in the wild…",
      "severity_cvss": 9.8,
      "severity_label": "critical",
      "vendors": [],
      "products": [],
      "target_countries": [],
      "exploitation_status": null,
      "sectors": []
    }
  ],
  "next_cursor": "eyJ0cyI6IjIwMjYtMDUtMjBUMTA6NDU6MDBaIiwiaWQiOjQ4MjMwMX0="
}

POST /digest/subscribe

Subscribe to the daily email digest

Body is JSON: { email, vendors?, severity?, sectors? }. Triggers a double-opt-in email — subscription is only active once /digest/verify is hit with the token. The API never returns email addresses or PII.

Parameters

Name Type Description Example
email (required) string (body) Recipient address. Validated with a permissive RFC-5322 regex. [email protected]
vendors csv (body) Restrict the digest to items matching these vendor slugs.
severity csv (body) Restrict to severities (critical|high|medium|low|info).
sectors csv (body) Restrict to one or more sector slugs.

Example request

curl https://api.threatfilter.dev/digest/subscribe

Example response

{
  "ok": true,
  "message": "verification email queued"
}

GET /digest/unsubscribe

Unsubscribe from the daily digest

Single-click unsubscribe link in every digest email. Requires the per-subscriber token issued at subscribe time. No body, no auth header.

Parameters

Name Type Description Example
token (required) string Opaque per-subscriber unsubscribe token from the digest footer.

Example request

curl https://api.threatfilter.dev/digest/unsubscribe?token=…

Example response

{
  "ok": true,
  "message": "unsubscribed"
}