EPSS::Client
High-level client for the FIRST EPSS REST API
(https://api.first.org/data/v1/epss).
client = EPSS::Client.new
resp = client.fetch(EPSS::Query.new(cves: ["CVE-2022-27225"]))
resp.scores.first.epss # => 0.001870
Convenience helpers cover the common cases without constructing a
Query:
client.score("CVE-2022-27225") # => EPSS::Score?
client.scores(["CVE-1", "CVE-2"]) # => Array(EPSS::Score)
client.each_score(query) { |s| ... } # paginated stream
Constants
Upper bound for a single exponential-backoff sleep. Without a cap the doubling delay grows unbounded and can strand a fiber for minutes; 30s is plenty to let a transient outage clear.
Constructors
Instance methods
Materialize every result matching query into a single Array.
Convenience wrapper around #each_score โ be aware that an
unfiltered query can be hundreds of thousands of rows.
Exponential backoff with a hard cap and decorrelated jitter: base * 2^(attempt-1), clamped to MAX_BACKOFF, plus up to 10% jitter.
The cap keeps a long retry chain from sleeping for minutes, and the jitter spreads concurrent retries so they don't all wake at once. This is library runtime code (not a deterministic workflow script), so a random source is acceptable here.
Public so the cap/jitter bounds can be unit-tested without sleeping.
Compose the absolute URI for a query against this client's base.
Iterate every score matching query, transparently fetching
subsequent pages while results remain. Uses the API's limit /
offset parameters; the iteration order matches the server's
response order (controlled by query.order).
Issue a single request and return the decoded Response. Does not
iterate pages โ use #each_score or #all_scores for that.
Download and parse the daily EPSS feed for date using this client's
transport, retry policy, User-Agent, and timeouts. The feed is the
second canonical EPSS distribution channel; routing it through the
same pipeline as the JSON API means a slow network surfaces as
APIError instead of an unbounded hang.
feed = client.fetch_feed(Time.utc(2026, 5, 18))
feed.scores.size # => 240000+
Return the (at most one) score for a single CVE on the latest day,
or nil if FIRST has no published score for it.
Look up multiple CVEs in one call. The API caps the URL length, so
this helper batches into chunks of batch_size (default 100) and
concatenates the results.
Fetch the full 30-day EPSS time-series for one CVE. Returns a flat
list of Scores โ one per day, sorted oldest-first.
series = client.time_series("CVE-2022-27225")
series.first.date # => 30 days ago
series.last.date # => today (or most recent publication)