EPSS vs CVSS: How to Use Exploit Prediction Scores to Prioritize Patching When You Cannot Patch Everything

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Every vulnerability management program faces the same problem: more CVEs than patch capacity. CVSS scores sort by potential severity but ignore exploitation probability -- a CVSS 9.8 with zero exploitation activity is less urgent than a CVSS 6.5 that is actively being exploited in ransomware campaigns. EPSS is the model that fills this gap. Used alongside CVSS and the CISA KEV catalog, it reduces a list of thousands of critical CVEs to a workable priority queue of CVEs that are both severe and likely to be used against you.
How EPSS Works and What the Score Means
EPSS is a machine learning model trained on CVE characteristics and observed exploitation data from multiple threat intelligence sources including exploit databases, malware repositories, and honeypot networks. It outputs a probability score between 0 and 1 representing the likelihood that a CVE will be observed in exploitation activity within the next 30 days.
Key properties:
- Score updates daily: EPSS scores change as new exploitation activity is observed. A CVE that had a score of 0.02 last month may be 0.87 this week because a PoC was published.
- Score is not permanent: High EPSS scores often decay after the initial exploitation wave. Monitor score changes, not just current scores.
- Percentile is often more useful than raw score: EPSS provides both a score and a percentile. A score of 0.05 sounds low but if it represents the 94th percentile of all CVEs, it means 94% of all CVEs are less likely to be exploited.
The EPSS API:
import requests
# Get EPSS score for a specific CVE
cve_id = 'CVE-2024-21762'
response = requests.get(f'https://api.first.org/data/v1/epss?cve={cve_id}')
data = response.json()
print(data['data'][0]) # {'cve': 'CVE-2024-21762', 'epss': '0.96813', 'percentile': '0.99988', 'date': '2026-06-12'}
# Bulk fetch for a list of CVEs
curl 'https://api.first.org/data/v1/epss?cve=CVE-2024-21762,CVE-2024-30078,CVE-2023-44487'
Building a Composite Priority Score: EPSS + CVSS + KEV
Neither EPSS nor CVSS alone is sufficient. The composite approach:
Tier 1 (patch within 24 hours):
- CVE is in the CISA KEV catalog (confirmed active exploitation)
- Regardless of CVSS or EPSS score -- KEV means it is being exploited right now
Tier 2 (patch within 7 days):
- EPSS score above 0.5 (50% probability of exploitation in 30 days) AND
- CVSS score above 7.0 (High or Critical)
- This is the population of CVEs that are both severe and likely to be weaponized
Tier 3 (patch within 30 days per normal cycle):
- CVSS 9.0+ OR EPSS above 0.3, not in Tier 1 or 2
Tier 4 (patch in next scheduled maintenance):
- Everything else above CVSS 4.0
Practical implementation in Python:
import requests
def get_epss_score(cve_id):
r = requests.get(f'https://api.first.org/data/v1/epss?cve={cve_id}')
data = r.json().get('data', [])
return float(data[0]['epss']) if data else 0.0
def prioritize_cve(cve_id, cvss_score, in_kev=False):
epss = get_epss_score(cve_id)
if in_kev:
return 1, 'Tier 1 - Patch in 24h (KEV confirmed)'
if epss >= 0.5 and cvss_score >= 7.0:
return 2, f'Tier 2 - Patch in 7 days (EPSS={epss:.2f}, CVSS={cvss_score})'
if cvss_score >= 9.0 or epss >= 0.3:
return 3, f'Tier 3 - Patch in 30 days (EPSS={epss:.2f}, CVSS={cvss_score})'
return 4, f'Tier 4 - Normal cycle (EPSS={epss:.2f}, CVSS={cvss_score})'
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Integrating EPSS into Your Existing Vulnerability Scanner Output
Most vulnerability scanners (Tenable, Qualys, Rapid7) export CVE lists in CSV or JSON. Enrich the scanner output with EPSS scores before generating your patch priority list.
Bulk EPSS enrichment script (Python):
import requests
import csv
import time
def enrich_cve_list(input_csv, output_csv):
with open(input_csv) as f:
rows = list(csv.DictReader(f))
cve_ids = [row['CVE'] for row in rows if row.get('CVE')]
# EPSS API supports up to 1000 CVEs per request
epss_data = {}
for i in range(0, len(cve_ids), 100):
batch = cve_ids[i:i+100]
cve_param = ','.join(batch)
r = requests.get(f'https://api.first.org/data/v1/epss?cve={cve_param}')
for item in r.json().get('data', []):
epss_data[item['cve']] = (item['epss'], item['percentile'])
time.sleep(0.5) # Respect rate limits
# Write enriched output
with open(output_csv, 'w', newline='') as f:
fieldnames = rows[0].keys() | {'EPSS_Score', 'EPSS_Percentile'}
writer = csv.DictWriter(f, fieldnames=fieldnames)
writer.writeheader()
for row in rows:
cve = row.get('CVE', '')
epss, pct = epss_data.get(cve, ('0', '0'))
row['EPSS_Score'] = epss
row['EPSS_Percentile'] = pct
writer.writerow(row)
enrich_cve_list('scanner_output.csv', 'enriched_vulns.csv')
After enrichment, sort by EPSS_Score descending within each CVSS severity band to get your priority queue.
Monitoring EPSS Score Changes for Trending CVEs
A CVE that jumps from EPSS 0.02 to 0.85 in 48 hours indicates active weaponization -- a PoC was published, a campaign started, or it entered a widely-used exploit kit. Monitoring score changes is as important as monitoring current scores.
Daily delta monitoring:
import requests
from datetime import date, timedelta
def get_epss_on_date(cve_id, date_str):
r = requests.get(f'https://api.first.org/data/v1/epss?cve={cve_id}&date={date_str}')
data = r.json().get('data', [])
return float(data[0]['epss']) if data else 0.0
def check_score_increase(cve_id, threshold=0.3):
today = str(date.today())
yesterday = str(date.today() - timedelta(days=1))
current = get_epss_on_date(cve_id, today)
previous = get_epss_on_date(cve_id, yesterday)
delta = current - previous
if delta >= threshold:
print(f'ALERT: {cve_id} EPSS increased by {delta:.3f} (now {current:.3f})')
return delta
Practical alert threshold: A CVE jumping 0.3 or more in a single day warrants immediate review. If that CVE affects systems in your environment and the new score exceeds 0.5, escalate to Tier 1 or Tier 2 treatment regardless of when it was originally scheduled for patching.
The bottom line
EPSS changes patch prioritization from 'sort by CVSS score and work down the list' to 'patch the things that are actually being exploited.' The composite workflow: KEV catalog for confirmed exploitation (patch in 24 hours), EPSS above 0.5 plus CVSS above 7.0 for likely imminent exploitation (patch in 7 days), and normal cycles for everything else. The EPSS API is free, returns results in under a second, and can be integrated into any scanner CSV export with 20 lines of Python. Monitor daily score changes for sudden spikes that indicate newly weaponized CVEs.
Frequently asked questions
What EPSS score threshold should I use for emergency patching?
FIRST.org does not publish a single threshold recommendation because the right threshold depends on your patch capacity and risk tolerance. Common operational thresholds: 0.5 (high confidence -- 50%+ probability in 30 days) for escalating to a 7-day patch SLA, and 0.3 (moderate probability) for escalating within the current patch cycle. For organizations that are currently only patching CVSS 9+, adding an EPSS filter of 0.5+ to CVSS 7+ is a practical starting point that typically reduces the list by 60-80% while covering the high-exploitation-probability population.
Why does EPSS sometimes give a low score to CVEs that are being actively exploited?
EPSS is a probability model trained on observed exploitation signals. It can lag behind very new exploitation waves -- if a CVE just entered active exploitation but that signal has not propagated through the threat intelligence feeds EPSS ingests, the score may be low for a day or two. This is why the CISA KEV catalog is a separate first-tier signal -- it represents confirmed exploitation that a human at CISA has verified, without the model lag. Use EPSS and KEV together; do not rely on EPSS alone to catch zero-day or very-recently-weaponized CVEs.
Is EPSS available for CVEs that CVSS has not scored yet?
Yes, as of EPSS v4 (March 2025). EPSS v4 scores CVEs in the NVD backlog that do not yet have an official CVSS score. This addresses one of the practical gaps in earlier versions -- organizations could not get EPSS scores for new CVEs that were in the NVD analysis queue. Check the EPSS API for the CVE ID directly; if it returns a score, the model has sufficient data to estimate exploitation probability regardless of NVD scoring status.
Can I use EPSS to justify not patching low-CVSS vulnerabilities?
EPSS is a tool for prioritizing when to patch, not whether to patch. A CVE with EPSS 0.001 (very unlikely to be exploited) and CVSS 4.0 is still a vulnerability in your environment that should be remediated -- just not at the expense of the EPSS 0.9, CVSS 7.5 CVE waiting in the queue. Using EPSS to indefinitely defer low-probability CVEs creates technical debt. The defensible use is: EPSS high + CVSS high = patch immediately, EPSS low + CVSS low = patch in normal cycle, never = never patch.
How do I integrate EPSS scores into an existing vulnerability management tool?
The EPSS API at api.first.org/epss provides a JSON endpoint that returns current and historical EPSS scores for any CVE ID. Most vulnerability management platforms (Tenable, Rapid7, Qualys) have begun incorporating EPSS natively in their scoring displays, but for platforms that do not, you can supplement via API integration: fetch EPSS scores for all CVEs in your open findings using the bulk endpoint (accepts comma-separated CVE IDs up to 100 per request), store the EPSS score alongside the existing CVSS score, and sort findings by a composite score formula such as `(CVSS/10 * 0.4) + (EPSS * 0.6)`. Refresh EPSS scores weekly since they update as new exploitation signals emerge.
What is the CISA Known Exploited Vulnerabilities catalog and how should I use it in my patching workflow?
The CISA KEV catalog (cisa.gov/known-exploited-vulnerabilities-catalog) is a maintained list of vulnerabilities that CISA has confirmed are actively exploited in the wild. Federal agencies are required to remediate KEV entries within 2 weeks (for Critical) or 3 weeks (for other severities) under Binding Operational Directive 22-01. For non-federal organizations, the KEV serves as a high-confidence signal for prioritization: a vulnerability on the KEV list has confirmed active exploitation, removing the uncertainty that makes CVSS and EPSS probabilistic. Integrate the KEV into your vulnerability management workflow by: downloading the KEV JSON feed (cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json) and cross-referencing it against your open findings daily, creating a separate SLA for KEV findings that is shorter than your standard critical finding SLA (24-72 hours for KEV entries versus 7-14 days for non-KEV critical), and flagging KEV status in your vulnerability dashboard separately from CVSS and EPSS scores. A finding that is KEV-listed at any CVSS score should be treated as higher priority than a non-KEV CVSS 10.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
