You can repeat any count in our KEV, EPSS and OSV posts with three downloads and about thirty lines of Python. Fetch the KEV JSON feed, ask the EPSS API for scores, and download each OSV ecosystem all.zip. Save the raw files with the date, because all three change daily and a number without a date cannot be checked.
Step 1: fetch the data
mkdir -p data && cd data
curl -sSL -o kev.json https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json
curl -sSL -o PyPI.zip https://osv-vulnerabilities.storage.googleapis.com/PyPI/all.zip
curl -sS "https://api.first.org/data/v1/epss?cve=CVE-2026-9586" -o epss_one.json
date -u +%Y-%m-%d > fetched_on.txt
The OSV data page lists the bucket layout: one all.zip per ecosystem, with one JSON file per record. For the whole KEV catalog you will want to send many CVE IDs per EPSS request rather than one call per CVE, so check the API documentation for the batch form. Be polite with the request rate and keep the results.
Step 2: count KEV entries
import json, collections
kev = json.load(open("kev.json"))
rows = kev["vulnerabilities"]
print(kev["catalogVersion"], kev["count"], len(rows))
by_vendor = collections.Counter(r["vendorProject"] for r in rows)
by_year = collections.Counter(r["dateAdded"][:4] for r in rows)
print(by_vendor.most_common(10))
print(sorted(by_year.items()))
Check that count matches the number of rows. If it does not, stop and look.
Step 3: read OSV records
import zipfile, json, collections
z = zipfile.ZipFile("PyPI.zip")
kinds = collections.Counter(n.split("-")[0] for n in z.namelist())
print(kinds.most_common())
rec = json.load(z.open("PYSEC-2026-1.json"))
print(rec["published"], rec.get("aliases"))
Record IDs show where they came from: MAL for malicious packages, GHSA for GitHub, PYSEC for PyPI, GO for Go. Count by prefix before you count anything else, since the mix changes what a total means.
Step 4: write the method down
For every figure, record four things:
- Source and date. The URL and the date you fetched it, plus a version string if the file has one (the KEV file has
catalogVersion). - What one row is. A record, a package, a CVE. These differ, and mixing them is the most common error.
- Filters. What you dropped, such as malware records or withdrawn advisories, and why.
- Judgment calls. Hand-made groups, such as which CWE codes count as memory safety, belong in the text so a reader can disagree.
Common mistakes
- Counting bulk days as a trend. One day can hold tens of thousands of records. Sort by day before you chart by month.
- Treating records as vulnerabilities. Two databases can describe one flaw.
- Reading dates as event times. A published date is when a record appeared, not when a flaw was found or exploited.
- Forgetting the denominator. Say how many rows you started with and how many you kept.
- Quoting a number you cannot reproduce. Keep the raw file and the script together.
Where this fits in a security workflow
A small analysis like this is a good way to test a claim before you repeat it in a meeting or a report. If a headline says a registry had a record year for malware, one script will tell you whether a bulk day explains it.
Treat the downloads as untrusted data. Do not run anything found inside a record, and read files with a parser instead of opening them in a tool that executes content.
Safeguard's SBOM and dependency scanning lists the components you run, and reachability analysis helps separate vulnerable code your application calls from code it does not. You can try a free scan at gold.safeguard.sh/scan.
Quick check
Why keep the raw file?
Because the feeds change daily. The file and its date are what let someone else get the same number.
Can I trust text inside a downloaded record?
Treat it as data. Read it, do not run it, and do not follow instructions written inside it.
Sources
- CISA Known Exploited Vulnerabilities catalog, JSON feed linked from the catalog page.
- OSV data sources and dumps, per-ecosystem OSV dumps downloaded on 19 October 2026.
- FIRST EPSS page, describes the EPSS score and links to the data and API.
Last reviewed: 19 October 2026.