Build with the API / Exploited this week
Exploited this week
CVEs from the last 7 days that are in KEV or have a public exploit.
What you get
Combines two views of the last 7 days of CVEs, the ones CISA added to the Known Exploited Vulnerabilities catalog and the ones with a known public exploit, into one table sorted by CVSS, with a link to each CVE's ThreatCluster page.
- Endpoints
/vulnerabilities- Credits per run
- 2
- Key
- Works on the free key
This week, live
What the script returns right now, refreshed hourly. CVEs published in the last seven days that CISA lists as exploited or that have a public exploit, highest CVSS first.
| CVE | CVSS | EPSS | Why it is here |
|---|---|---|---|
CVE-2026-57155 | 9.9 critical | 1% | public exploit |
CVE-2026-59774 | 9.8 critical | 28% | public exploit |
CVE-2026-76519 | 9.8 critical | 2% | KEV |
CVE-2026-102764 | 9.8 critical | 1% | KEV, public exploit |
CVE-2026-104748 | 9.8 critical | 1% | public exploit |
CVE-2026-89079 | 9.8 critical | 0% | public exploit |
CVE-2026-99999 | 9.3 critical | 0% | public exploit |
CVE-2026-94134 | 9.1 critical | 0% | public exploit |
CVE-2026-96951 | 8.8 high | 2% | KEV |
CVE-2026-104935 | 8.6 high | 26% | KEV, public exploit |
CVE-2026-0322 | 7.2 high | 1% | KEV |
Step by step
1. Get a key
Sign in, open Settings, then API & Feeds, and select Generate API key. The key is shown once, so copy it. Every account has one and the free key covers this recipe.

The API page. The key itself is created in Settings, under API & Feeds. 2. Download the script and set the key
The script is one file with one dependency. It reads the key from the environment, so the key never goes into the file.
curl -sO https://raw.githubusercontent.com/Jam0k/Cyber-Threat-Intelligence-API/main/examples/recipes/exploited-this-week.py pip install requests export THREATCLUSTER_API_KEY=tc_live_your_key_here3. Run it
No arguments. The script lists CVEs published in the last 7 days that are in CISA KEV or have a public exploit, highest CVSS first.
python3 exploited-this-week.py
Run on 29 September 2026. 4 CVEs from that week were in KEV and 8 had a public exploit. 4. Open a CVE
Each row links to the CVE's page.

A Check Point CVE that was both in KEV and had a public exploit that week. 5. Post it to the team on Mondays
The output is plain text, so it pipes into mail or any chat webhook. The run costs 2 credits.
python3 exploited-this-week.py | mail -s "Exploited this week" [email protected]
The code
exploited-this-week.py, 95 lines. Python 3, needs requests.
#!/usr/bin/env python3
"""exploited-this-week: CVEs from the last 7 days that are in KEV or have a public exploit.
What it does
Combines two views of the last 7 days of CVEs, the ones CISA added to the Known
Exploited Vulnerabilities catalog and the ones with a known public exploit, into one
table sorted by CVSS, with a link to each CVE's ThreatCluster page.
Endpoints
GET /vulnerabilities?days=7&kev_only=true (1 credit)
GET /vulnerabilities?days=7&has_exploit=true (1 credit)
Cost: 2 credits per run. Works on the free tier (25 rows per list).
Usage
python3 exploited-this-week.py
"""
import json
import os
import sys
import time
import requests
API_BASE = os.environ.get("THREATCLUSTER_API_BASE", "https://threatcluster.io/api/public/v1").rstrip("/")
API_KEY = os.environ.get("THREATCLUSTER_API_KEY", "")
SITE = os.environ.get("THREATCLUSTER_SITE", "https://threatcluster.io") # where the linked pages live
PACE_SECONDS = 2.1 # free keys get 30 requests/min; ~28/min keeps loops clear of 429s
_credits = {"used": 0, "remaining": None}
def _auth_headers():
# A tc_... key is sent as X-API-Key; anything else is a short-lived bearer (e.g. from `tc login`).
if not API_KEY:
sys.exit("THREATCLUSTER_API_KEY is not set. Free keys: https://threatcluster.io/about/api")
if API_KEY.startswith("tc_"):
return {"X-API-Key": API_KEY}
return {"Authorization": "Bearer " + API_KEY}
def api_get(path, _allow=(), **params):
"""GET one endpoint. Exits non-zero with the API's own error body, unless the
status is listed in _allow (per-item lookups where a 404 is itself an answer)."""
r = requests.get(API_BASE + path, headers=_auth_headers(), params=params, timeout=60)
if "X-Request-Cost" in r.headers:
_credits["used"] += int(r.headers["X-Request-Cost"])
if "X-RateLimit-Remaining" in r.headers:
_credits["remaining"] = r.headers["X-RateLimit-Remaining"]
if r.status_code >= 400 and r.status_code not in _allow:
try:
body = json.dumps(r.json())
except ValueError:
body = r.text[:500]
sys.exit("HTTP %d from %s: %s" % (r.status_code, path, body))
return r
def cluster_url(cluster):
# The free payload carries `slug` (canonical page) and `cluster_id`; the last 8 hex
# chars of the id also resolve, via a 301 to the slug, when slug is missing.
ident = cluster.get("slug") or cluster.get("short_id") or cluster.get("cluster_id", "").replace("-", "")[-8:]
return "%s/cluster/%s" % (SITE, ident)
def report_credits():
# Stderr, so it never pollutes piped output (brief.md, blocklists, JSON).
# Keys without a daily cap send no remaining-credits header.
left = ", remaining today: %s" % _credits["remaining"] if _credits["remaining"] is not None else ""
sys.stderr.write("[threatcluster] credits used this run: %d%s\n" % (_credits["used"], left))
def main():
kev = api_get("/vulnerabilities", days=7, kev_only="true", limit=25).json()
time.sleep(PACE_SECONDS)
poc = api_get("/vulnerabilities", days=7, has_exploit="true", limit=25).json()
merged = {}
for v in kev.get("cves", []) + poc.get("cves", []):
merged.setdefault(v["cve_id"], v)
rows = sorted(merged.values(), key=lambda v: (-(v.get("cvss_v3_score") or 0), v["cve_id"]))
print("Last 7 days: %s CVE(s) in KEV, %s with a public exploit (showing up to 25 of each)\n"
% (kev.get("total", "?"), poc.get("total", "?")))
print("%-16s %5s %-9s %-4s %-4s %-10s %s" % ("CVE", "CVSS", "severity", "KEV", "PoC", "published", "ThreatCluster page"))
for v in rows:
print("%-16s %5s %-9s %-4s %-4s %-10s %s/entities/cve/%s" % (
v["cve_id"], v.get("cvss_v3_score") if v.get("cvss_v3_score") is not None else "-",
v.get("cvss_v3_severity") or "-", "yes" if v.get("in_kev") else "no",
"yes" if v.get("has_exploit") else "no", (v.get("published_date") or "")[:10], SITE, v["cve_id"]))
print("\n%d unique CVE(s)." % len(rows))
report_credits()
if __name__ == "__main__":
main()
The shell one-liner, exploited-this-week.sh:
#!/usr/bin/env bash
# exploited-this-week.sh: last-7-day CVEs that are in KEV or have a public exploit, sorted by CVSS. 2 credits. Free tier OK.
# usage: ./exploited-this-week.sh
set -euo pipefail
: "${THREATCLUSTER_API_KEY:?set THREATCLUSTER_API_KEY (free keys: https://threatcluster.io/about/api)}"
BASE="${THREATCLUSTER_API_BASE:-https://threatcluster.io/api/public/v1}"
SITE="${THREATCLUSTER_SITE:-https://threatcluster.io}"
# A tc_... key goes in X-API-Key; anything else is a bearer (e.g. from `tc login`).
if [[ "$THREATCLUSTER_API_KEY" == tc_* ]]; then AUTH="X-API-Key: $THREATCLUSTER_API_KEY"; else AUTH="Authorization: Bearer $THREATCLUSTER_API_KEY"; fi
# tc_get <path> [curl -G args...]: prints the body; on HTTP >= 400 prints the API error to stderr and fails.
tc_get() { local path=$1; shift; local out code
out=$(curl -sS -G -H "$AUTH" -w $'\n%{http_code}' "$BASE$path" "$@"); code=${out##*$'\n'}
if [ "$code" -ge 400 ]; then echo "HTTP $code from $path: ${out%$'\n'*}" >&2; return 1; fi; printf '%s' "${out%$'\n'*}"; }
{ tc_get /vulnerabilities --data-urlencode days=7 --data-urlencode kev_only=true --data-urlencode limit=25; sleep 2.1
tc_get /vulnerabilities --data-urlencode days=7 --data-urlencode has_exploit=true --data-urlencode limit=25; } \
| jq -r --arg site "$SITE" '.cves[] | "\(.cvss_v3_score // 0)\t\(.cve_id)\t\(.cvss_v3_severity // "-")\tKEV=\(.in_kev)\tPoC=\(.has_exploit)\t\($site)/entities/cve/\(.cve_id)"' \
| sort -t$'\t' -k2,2 -u | sort -t$'\t' -k1,1nr
Output
From a run on 29 September 2026. Yours will differ as the corpus moves.
Last 7 days: 4 CVE(s) in KEV, 8 with a public exploit (showing up to 25 of each) CVE CVSS severity KEV PoC published ThreatCluster page CVE-2026-12227 9.8 CRITICAL no yes 2026-09-24 https://threatcluster.io/entities/cve/CVE-2026-12227 CVE-2026-14281 9.8 CRITICAL no yes 2026-09-25 https://threatcluster.io/entities/cve/CVE-2026-14281 CVE-2026-18143 9.8 CRITICAL no yes 2026-09-26 https://threatcluster.io/entities/cve/CVE-2026-18143 CVE-2026-82384 9.8 CRITICAL no yes 2026-09-28 https://threatcluster.io/entities/cve/CVE-2026-82384 CVE-2026-88771 9.8 CRITICAL yes no 2026-09-27 https://threatcluster.io/entities/cve/CVE-2026-88771 CVE-2026-94127 9.8 CRITICAL yes no 2026-09-22 https://threatcluster.io/entities/cve/CVE-2026-94127 CVE-2026-84388 9.6 CRITICAL no yes 2026-09-22 https://threatcluster.io/entities/cve/CVE-2026-84388 CVE-2026-89055 9.1 CRITICAL no yes 2026-09-25 https://threatcluster.io/entities/cve/CVE-2026-89055 CVE-2026-93399 9.1 CRITICAL no yes 2026-09-25 https://threatcluster.io/entities/cve/CVE-2026-93399 CVE-2026-87902 8.1 HIGH yes yes 2026-09-22 https://threatcluster.io/entities/cve/CVE-2026-87902 CVE-2026-88772 8.1 HIGH yes no 2026-09-27 https://threatcluster.io/entities/cve/CVE-2026-88772 11 unique CVE(s). [threatcluster] credits used this run: 2
The file on GitHub: exploited-this-week.pyand exploited-this-week.sh.
Goes well with
- CVE triage: KEV and EPSS filter: From a list of CVE ids, keep only the ones that matter right now.
- A lookup tool for LLM agents: A tool-calling function `threatcluster_lookup(query)` for LLM agents.
- Brand and domain dark-web monitor: Are my brands or domains showing up on the dark web?
- curl integration: the plain HTTP contract, headers and status codes
All recipes: /build. Endpoint reference: /api/public/v1/docs.