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 /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
}
]
}
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"
}