Docs / Cyber Threat Data API / Endpoints / Which score set is served
Which score set is served
Says which score set is being served right now — score date, model version, the dated file it came out of and the sha256 of that file's gzipped bytes as served — in one request, instead of paging 377,000 rows to find out. Use it to decide whether to re-pull a cache, to verify that what we served is what FIRST published, and to get the address of the bulk file if what you actually want is all of the scores rather than a hundred of them. It also carries the licence and the attribution, and that is a compliance obligation rather than a courtesy: EPSS is CC BY 4.0, which binds anyone who redistributes it, and the string is FIRST's own carried verbatim. `score_date` here is the newest day we hold. Each score row carries its own, and after a run that failed part-way some rows are a day behind — so for any single row, that row's `score_date` is the authority.
Request
curl https://api.softon.dev/v1/cyber/epss/snapshot \
-H "Authorization: Bearer $SOFTON_KEY"
req, _ := http.NewRequestWithContext(ctx, "GET", "https://api.softon.dev/v1/cyber/epss/snapshot", nil) req.Header.Set("Authorization", "Bearer "+os.Getenv("SOFTON_KEY")) res, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer res.Body.Close() var page struct { Data Snapshot `json:"data"` Meta struct{} `json:"meta"` } if err := json.NewDecoder(res.Body).Decode(&page); err != nil { log.Fatal(err) }
import json, os, urllib.parse, urllib.request url = "https://api.softon.dev/v1/cyber/epss/snapshot" req = urllib.request.Request(url, headers={ "Authorization": "Bearer " + os.environ["SOFTON_KEY"], }) page = json.load(urllib.request.urlopen(req)) # page["data"] is the object; page["error"] is None on success
const url = new URL("https://api.softon.dev/v1/cyber/epss/snapshot"); const res = await fetch(url, { headers: { Authorization: `Bearer ${process.env.SOFTON_KEY}` }, }); if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); const { data, meta } = await res.json();
Response
The envelope is identical on every softon.dev API: data, meta, error. Only the shape inside data changes per dataset — see the response envelope.
{
"data": {
"source": "epss",
"score_date": "2026-09-20",
"model_version": "v2026.06.15",
"upstream_url": "https://epss.empiricalsecurity.com/epss_scores-2026-09-20.csv.gz",
"checksum_sha256": "6e5d46d4fa7f7229fa41813e01ea9a56221fa082244323470a70911e984275b6",
"licence": "CC-BY-4.0",
"attribution": "EPSS scores provided by the Forum of Incident Response and Security Teams (FIRST) — https://www.first.org/epss/",
"ingested_at": "2026-09-21T02:00:41.207318Z"
},
"meta": { "request_id": "req_9Fv3" },
"error": null
}
Fields of data
| Field | Type | Description |
|---|---|---|
source |
string | The scraper stem, `epss`. |
score_date |
date | The day of the newest score set held. After a clean run every row shares it; after a run that failed mid-batch some rows are older, so this is **the newest day we hold** and each row's own `score_date` stays the authority for that row. |
model_version |
string | The model that produced that day's scores, `v2026.06.15`. |
upstream_url |
string | The dated file, resolved past the redirect chain. **This is the address of the bulk data**: if you want every score rather than a hundred, fetch this from FIRST once a day rather than paging an API. |
checksum_sha256 |
string | `sha256` of that file's gzipped bytes as served. Verify that what we served is what FIRST published. |
licence |
string | SPDX identifier, `CC-BY-4.0`. **This is an attribution obligation that travels with the data**, not a note about our terms: if you publish these scores, reproduce `attribution`. |
attribution |
string | The string to reproduce, FIRST's own and carried verbatim. An attribution somebody rewrote is not the attribution that was asked for. |
ingested_at |
timestamp | When this platform stored that snapshot. |
Try it
Send the request to see a response.