Developers

Data dictionary

Every machine-readable file endoflife.ai publishes, field by field
This page documents the structure of each public data file: its URL, format, how often it is rebuilt, its top-level layout and every field, with the field's type, where its value comes from and what it means. Meanings are taken from the code that writes each file. The example records were copied from the files published on 16 October 2026 and are there to show shape only: values change with every build, so read the live file for current values.

Contents

How to read this page

Where each value comes from

Every field row carries one of these labels:

A few fields carry other sources, named in the row: FIRST.org EPSS, the NIST CPE dictionary, package registries.

Refresh

The site is rebuilt on every published change and by a scheduled daily rebuild, so a file marked every build is regenerated at least once a day. Each file says when it was built: a generated or build_date field, or the Built date in its notice. Where a file is republished every build but its content is regenerated less often, the row says so.

Conventions

Licence and attribution

This section repeats what the files and the terms of service already say. It adds no term of its own.

The notice in each file

Each bulk file that contains third-party data carries a notice naming the sources it contains and pointing to our terms:

The _license object has these fields:

FieldTypeSourceMeaning
publisherstringMetadataAlways endoflife.ai.
sourcesarray of stringMetadataOne sentence per third-party source present in the file: the endoflife.date line, the CISA KEV line, or both.
termsstring (URL)Metadatahttps://endoflife.ai/terms.
noticestringMetadataAlways Use of this file is subject to the terms at that URL.
scopestringMetadataThe data licence scope sentence, the same in every file and on the terms page (quoted below).
build_datestring (date)MetadataThe day the file was built.

The full notice, as it appears in scores.json.LICENSE.txt:

endoflife.ai scores.json. Includes data from endoflife.date, MIT licence (https://github.com/endoflife-date/endoflife.date/blob/master/LICENSE). CISA Known Exploited Vulnerabilities catalog, public domain (https://www.cisa.gov/known-exploited-vulnerabilities-catalog). Terms: https://endoflife.ai/terms. Use of this file is subject to the terms at that URL. Licence scope: free for evaluation and for running, securing and planning your own IT estate, including through the official endoflife.ai integrations; a data licence from [email protected] is required to use endoflife.ai data to assess, rate, underwrite, audit, research or report on other organisations, in any product, service or deliverable for third parties, to redistribute or resell it, or to store it in bulk to build a dataset. Data that originates from endoflife.date stays under its MIT licence and CISA KEV records stay public domain; the licence requirement applies only to what endoflife.ai adds. Built 2026-10-16.

What the terms of service say

The data licence (/terms#data-licence), whose scope sentence every notice carries:

Licence scope: free for evaluation and for running, securing and planning your own IT estate, including through the official endoflife.ai integrations; a data licence from [email protected] is required to use endoflife.ai data to assess, rate, underwrite, audit, research or report on other organisations, in any product, service or deliverable for third parties, to redistribute or resell it, or to store it in bulk to build a dataset. Data that originates from endoflife.date stays under its MIT licence and CISA KEV records stay public domain; the licence requirement applies only to what endoflife.ai adds.
Lifecycle data that originates from endoflife.date is provided under the MIT License. You may use this data subject to the terms of the MIT License and these Terms of Service.
Products and services that display data from the endoflife.ai API must include visible attribution to endoflife.ai. The EOL Risk Score™ name and methodology may not be used without a formal partnership agreement.
The EOL Risk Score™ name, methodology, scoring factors, and weights are proprietary to endoflife.ai. EOL Risk Score™ is a trademark of endoflife.ai, established through use. You may not reproduce, publish, or implement the EOL Risk Score™ methodology without our written permission.

The full text, including the API's prohibited uses, is at /terms.

Files with their own statement

exploited-and-unpatchable.json carries a license field that reads:

Aggregated from public sources (CISA KEV, NVD, vendor advisories, FIRST.org EPSS) and endoflife.ai verified lifecycle data. Attribution required: endoflife.ai. EPSS data © FIRST.org. Terms: https://endoflife.ai/terms. Licence scope: free for evaluation and for running, securing and planning your own IT estate, including through the official endoflife.ai integrations; a data licence from [email protected] is required to use endoflife.ai data to assess, rate, underwrite, audit, research or report on other organisations, in any product, service or deliverable for third parties, to redistribute or resell it, or to store it in bulk to build a dataset. Data that originates from endoflife.date stays under its MIT licence and CISA KEV records stay public domain; the licence requirement applies only to what endoflife.ai adds.

eos-edge-devices.json and .csv point (in feed.license) to /eos-edge-devices#license, which says:

Reading and evaluating the files is free, and so is using them to run, secure and plan your own IT estate, including through the official endoflife.ai integrations. Using the data to assess, rate, underwrite, audit, research or report on other organisations, embedding it in a product, service or deliverable for third parties, redistributing or reselling it, or storing it in bulk to build a dataset is a licence (see the data licence terms): an annual agreement with redistribution rights, a stated refresh cadence, the change feed, and a named contact for vendor-date questions.

The other files (the calendars, changelog.json and .xml, exploited-and-unpatchable.xml, eos-edge-changelog.json, score-verify.json and the SBOM reference files) carry no notice of their own; the terms of service cover use of endoflife.ai. Questions about licensing the data go to [email protected].

scores.json

The product-level EOL Risk Score™ for every scored product. It is the same score the product pages show. How the score and its four factors are calculated is explained at /risk-score.

URL
https://endoflife.ai/scores.json
Format
JSON, application/json
Refresh
Every build
Notice
scores.json.LICENSE.txt (endoflife.date and CISA KEV lines)
Top level
An object keyed by product slug; each value is one score record. Nothing else is at the top level. It covers the products in checker-db.json plus the AI model provider pages.
FieldTypeSourceMeaning
scoreinteger 0–100ComputedThe product's EOL Risk Score: the sum of the four factors, capped at 100. Higher means more risk.
gradestringComputedLetter grade from the score: A 0–20, B 21–40, C 41–60, D 61–80, F 81–100.
labelstringComputedThe score's risk band in words, the same bands as the methodology and the API's band field: Low risk 0–25, Medium risk 26–50, High risk 51–75, Critical risk 76–100. It follows the score, not the letter: a score of 78 is grade D and Critical risk. (Until 2026-10-19 the label used the letter grade's steps.)
factors.recencyinteger 0–40ComputedEOL recency points: how long ago the product's line reached end of life, or how soon it will.
factors.surfaceinteger 0–30ComputedAttack surface points, from the product's category tier.
factors.kevinteger 0–20Computed from CISA KEVCISA KEV exposure points: 20 when the product is named in CISA's catalog, 10 when a core component it ships is, 0 otherwise.
factors.supportinteger 0–10ComputedExtended support points: 10 when no extended support option is known for the product, 0 when one is.
eolDatestring (date) or nullLifecycleThe end-of-life date the score is based on: the most recent end-of-life date among the product's ended lines; if no line has ended, the next scheduled one. null when that basis is not a dated value (a line ended with no published day, or no end date is published at all).
eolDatePrecisionstring, optionalLifecyclemonth or quarter when the vendor publishes eolDate to the month only; eolDate is then that month's last day (date precision). Absent = day precision.
nextEolDatestring (date) or nullLifecycleThe next scheduled end-of-life date among the product's lines still supported. null when no future end date is published. Use this, not eolDate, for "days until the next deadline": eolDate is the score's basis and is a past date once any line has ended.
nextEolDatePrecisionstring, optionalLifecyclemonth or quarter when nextEolDate is published to the month only. Absent = day precision.
updatedAtstring (date)MetadataThe build day that computed this record.

Example record

{
  "postgresql": {
    "score": 60,
    "grade": "C",
    "label": "Elevated risk",
    "factors": {
      "recency": 30,
      "surface": 30,
      "kev": 0,
      "support": 0
    },
    "eolDate": "2025-11-13",
    "updatedAt": "2026-10-16"
  }
}

Version-level scores (one per release line) are served by the API and on each /score/<slug>/<cycle> page; they are not in this file.

checker-db.json

One summary record per tracked product: the database behind the EOL Checker. Its keys are the complete list of product slugs.

URL
https://endoflife.ai/checker-db.json
Format
JSON, application/json
Refresh
Every build
Notice
checker-db.json.LICENSE.txt (endoflife.date and CISA KEV lines)
Top level
An object keyed by product slug; each value is one product record.
FieldTypeSourceMeaning
namestringMetadataThe product's display name.
versionstringComputedHow many release lines are tracked, as text (29 major versions tracked). Not a version number.
statusstringComputedeol when every tracked line is past end of life; warn when the next scheduled end of life is less than 90 days away; otherwise active.
eolDatestring (date) or nullLifecycleThe next scheduled end-of-life date among lines still supported; if none, the most recent dated end of life. null when neither exists.
eolDatePrecisionstring, optionalLifecyclemonth or quarter when the vendor publishes eolDate to the month only (date precision). Absent = day precision.
nextEolDatestring (date) or nullLifecycleThe next scheduled end-of-life date among lines still supported; null when none is scheduled (unlike eolDate, which then falls back to the most recent past end date).
releaseDatestring (date) or nullLifecycleRelease date of the oldest tracked line. Omitted when that line has no release date.
latestVersionstringLifecycleThe newest tracked line's latest release (for example 18.6), or the line's own name when no release number is published.
latestCyclestringLifecycleThe newest tracked line, as used in its page URL (/<slug>/<latestCycle>).
riskLevelstringComputedThe first word of the product's score label in capitals: LOW, MODERATE, ELEVATED, HIGH or CRITICAL. Same score as scores.json.
cpearray of string or nullNIST CPE dictionaryCPE 2.3 identifiers for the product, from cpe-map.json. null where no identifier represents it (the reason is in cpe-map.json).
purlarray of string or nullendoflife.date / registries / CuratedPackage URLs for the product, from purl-map.json. null where none is known.
summarystringComputedOne or two generated sentences: which lines are supported, how many are past end of life, and the next end of life.
recommendationstringComputedUpgrade to <name> <newest supported line>., or, when every line has ended, Migrate off <name> or obtain extended support.
patchesstringComputedWhich lines are actively supported, as text, or No versions receive security patches when every line has ended.
successornullMetadataReserved; currently always null.

Example record

{
  "postgresql": {
    "name": "PostgreSQL",
    "version": "29 major versions tracked",
    "status": "warn",
    "eolDate": "2026-11-12",
    "releaseDate": "1998-03-01",
    "latestVersion": "18.6",
    "latestCycle": "18",
    "riskLevel": "ELEVATED",
    "cpe": [
      "cpe:2.3:a:postgresql:postgresql"
    ],
    "purl": [
      "pkg:generic/postgresql",
      "pkg:docker/library/postgres",
      "pkg:deb/ubuntu/postgresql"
    ],
    "summary": "PostgreSQL 14–18 are supported. 24 versions are past end of life. Next EOL: PostgreSQL 14 on Nov 12, 2026.",
    "recommendation": "Upgrade to PostgreSQL 18.",
    "patches": "PostgreSQL 14–18 actively supported",
    "successor": null
  }
}

scanner-db.json

Every release line of every tracked product with its end-of-life value and status: the database behind the Stack Scanner.

URL
https://endoflife.ai/scanner-db.json
Format
JSON, application/json
Refresh
Every build
Notice
_license key (endoflife.date line)
Top level
_license, generated (build date), products (object keyed by product slug).
FieldTypeSourceMeaning
generatedstring (date)MetadataBuild date.
products.<slug>.namestringMetadataDisplay name.
products.<slug>.purlarray of stringendoflife.date / registries / CuratedPackage URLs, from purl-map.json. Omitted when the product has none.
products.<slug>.cycles[]array of objectLifecycleOne entry per tracked release line, newest first.
cycles[].cstringLifecycleThe release line (cycle) name.
cycles[].eolstring (date) or booleanLifecycleEnd-of-life value as served: a date, true (ended, day not published) or false (not ended, no date published). null if the value is anything else.
cycles[].pstring, optionalLifecyclemonth or quarter when the vendor publishes eol to the month only; eol is then that month's last day (date precision). Absent = day precision.
cycles[].sstringComputedStatus from eol, the same rule the site uses everywhere: eol when the date has passed or eol is true; warn when it is less than 180 days away; active otherwise.

Example record (the cycles array is cut to two entries here)

{
  "generated": "2026-10-16",
  "products": {
    "postgresql": {
      "name": "PostgreSQL",
      "purl": [
        "pkg:generic/postgresql",
        "pkg:docker/library/postgres",
        "pkg:deb/ubuntu/postgresql"
      ],
      "cycles": [
        { "c": "18", "eol": "2030-11-14", "s": "active" },
        { "c": "17", "eol": "2029-11-08", "s": "active" }
      ]
    }
  }
}

Note the two different warn windows: a line in scanner-db.json is warn under 180 days, while a product in checker-db.json is warn when its next end of life is under 90 days away.

calendar/<product>.ics

A subscribable calendar per product with every dated end of active support, end of life and end of extended support. Product pages link it as Subscribe to all dates (webcal://).

URL
https://endoflife.ai/calendar/<slug>.ics, for example /calendar/postgresql.ics
Format
iCalendar (RFC 5545), text/calendar, CRLF line endings, long lines folded
Refresh
Every build. The calendar asks clients to re-fetch daily (REFRESH-INTERVAL and X-PUBLISHED-TTL of one day).
Coverage
Written only for products with at least one dated event. Events from the past year onward are included; undated values are never turned into events.
PropertyTypeSourceMeaning
X-WR-CALNAMEtextMetadata<name> lifecycle dates (endoflife.ai).
X-WR-CALDESCtextMetadataWhat the calendar holds, and the product page URL.
VEVENT UIDtextMetadata<slug>-<cycle>-<field>@endoflife.ai, stable across builds, so a corrected date updates the existing event. <field> is support, eol or extendedSupport.
DTSTAMPtimestampMetadataWhen the build wrote the file.
DTSTART / DTENDdateLifecycleAn all-day event on the lifecycle date (DTEND is the following day, per RFC 5545).
Month-only datestextLifecycleWhen the vendor names only the month, the all-day event sits on the last day of that month, SUMMARY ends with the month in brackets (for example (October 2026)) and DESCRIPTION says the vendor names the month only (date precision).
SUMMARYtextMetadata<name> <line>: end of active support, : end of life or : end of extended support.
DESCRIPTIONtextMetadataThe same fact as a sentence with the date, and the version page URL, where the source of each date is shown.
URLURIMetadataThe version page, https://endoflife.ai/<slug>/<cycle>.
TRANSPtextMetadataAlways TRANSPARENT: events do not block time.

Example (calendar header and first event)

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//endoflife.ai//Lifecycle calendar//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:PostgreSQL lifecycle dates (endoflife.ai)
X-WR-CALDESC:End of active support\, end of life and extended support dates
  for PostgreSQL\, updated with every endoflife.ai build. https://endoflife
 .ai/postgresql
REFRESH-INTERVAL;VALUE=DURATION:P1D
X-PUBLISHED-TTL:P1D
BEGIN:VEVENT
UID:[email protected]
DTSTAMP:20261016T135215Z
DTSTART;VALUE=DATE:20301114
DTEND;VALUE=DATE:20301115
SUMMARY:PostgreSQL 18: end of life
DESCRIPTION:PostgreSQL 18 end of life on 2030-11-14\, per endoflife.ai life
 cycle data\; the page shows the source of each date and the day it was las
 t read. https://endoflife.ai/postgresql/18
URL:https://endoflife.ai/postgresql/18
TRANSP:TRANSPARENT
END:VEVENT

cpe-map.json

CPE (Common Platform Enumeration) identifiers for every product, so lifecycle data can be joined to scanner output and CVE records. Every product carries a decision: an identifier, or a recorded reason why none applies.

URL
https://endoflife.ai/cpe-map.json
Format
JSON, application/json
Refresh
Republished every build. The mapping itself is regenerated when the CPE pipeline runs; its date is generated.
Notice
_license key (endoflife.date line)
Top level
_license, generated, dictionary, about, counts, products.
FieldTypeSourceMeaning
generatedstring (date)MetadataWhen the mapping was last regenerated (not the build date).
dictionarystring (date) or nullMetadataDate of the NIST CPE dictionary copy the identifiers were checked against.
aboutstringMetadataA description of the file. It states that every identifier was verified to exist and to be non-deprecated in the NIST CPE dictionary.
counts.verifiedintegerMetadataProducts with a human-verified identifier.
counts.autointegerMetadataProducts with an automatic dictionary match.
counts.unmappedintegerMetadataProducts recorded as having no identifier.
counts.undecidedintegerMetadataProducts with no decision. The pipeline refuses to publish while this is above 0.
products.<slug>.cpearray of string or nullNIST CPE dictionaryCPE 2.3 identifiers truncated after the product component (cpe:2.3:<part>:<vendor>:<product>); version and the later components are left for the consumer. null when unmapped.
products.<slug>.sourcestringCurated / Computedverified: a human decision (an identifier, or a proven absence). auto: a strong automatic match against the NIST dictionary. The pipeline can also write policy or no-candidate for unmapped products; neither occurs in the current file.
products.<slug>.basisstringCuratedWhy this identifier was chosen. Present when cpe is set.
products.<slug>.unmappedbooleanCuratedtrue when no identifier represents the product. Present only then.
products.<slug>.reasonstringCuratedWhy there is no identifier, and how that was established. Present only when unmapped.

Example (a mapped and an unmapped product)

{
  "generated": "2026-10-15",
  "dictionary": "2026-09-09",
  "counts": {
    "verified": 267,
    "auto": 263,
    "unmapped": 139,
    "undecided": 0
  },
  "products": {
    "postgresql": {
      "cpe": [
        "cpe:2.3:a:postgresql:postgresql"
      ],
      "source": "auto",
      "basis": "Matched against the NIST dictionary; strong+clear-margin."
    },
    "alibaba-dragonwell": {
      "cpe": null,
      "unmapped": true,
      "source": "verified",
      "reason": "No triple for Alibaba Dragonwell under any vendor. Complete NIST dictionary (data/cpe-dictionary.json, 1.8M entries, pulled 2026-08-13) searched by name token and by vendor prefix on 2026-09-09."
    }
  }
}

purl-map.json

Package URLs (purl) that resolve an SBOM component to a tracked product. Match a component by its normalised purl through lookup; when a component has a purl, never fall back to its name.

URL
https://endoflife.ai/purl-map.json
Format
JSON, application/json
Refresh
Republished every build. The map is regenerated by its harvest; its date is generated.
Notice
_license key (endoflife.date line)
Top level
_license, generated, about, sources, counts, upstream_errors, unmapped, undisposed, products, lookup.
FieldTypeSourceMeaning
generatedstring (date)MetadataWhen the map was last harvested (not the build date).
aboutstringMetadataHow to use the map, including the normalisation rule.
sources.upstream / .registry / .overridestringMetadataWhere each kind of purl comes from: endoflife.date's product identifiers; the package ids our custom-product freshness check uses (npm, Maven, PyPI, GitHub); a recorded override with its own basis.
counts.products_with_purlintegerMetadataProducts with at least one purl.
counts.products_unmappedintegerMetadataProducts recorded as having none, with a reason in unmapped.
counts.products_undisposedintegerMetadataProducts with neither a purl nor a recorded reason.
counts.purlsintegerMetadataEntries in lookup.
counts.by_type.<type>integerMetadataPurls per package type (docker, npm, maven, rpm ...).
counts.upstream_products_fetched / upstream_errorsintegerMetadataendoflife.date product records read by the harvest, and how many failed.
upstream_errorsarrayMetadataThe products whose upstream read failed in the harvest.
unmapped.<slug>stringCuratedWhy the product has no purl.
undisposedarray of stringMetadataSlugs with no decision yet.
products.<slug>.purlarray of stringendoflife.date / registries / CuratedThe product's purls, without versions.
products.<slug>.sources.<purl>stringMetadataWhere that purl came from: upstream, registry or override.
products.<slug>.basisstringCuratedThe recorded evidence for a purl decision. Present on every product with an override and on a few others.
lookup.<purl>stringComputedNormalised purl (type and namespace/name, lowercase, no version, qualifiers or subpath) → product slug. Each purl points to exactly one product; the build fails if a key is not normalised or points to a product it did not build.

Example (one product, one unmapped product, one lookup entry)

{
  "products": {
    "postgresql": {
      "purl": [
        "pkg:generic/postgresql",
        "pkg:docker/library/postgres",
        "pkg:deb/ubuntu/postgresql"
      ],
      "sources": {
        "pkg:generic/postgresql": "upstream",
        "pkg:docker/library/postgres": "upstream",
        "pkg:deb/ubuntu/postgresql": "upstream"
      }
    }
  },
  "unmapped": {
    "adobe-acrobat": "Proprietary desktop application distributed only as Adobe installers and Creative Cloud downloads; no package-registry artefact (2026-10-10, name check only; no npm, NuGet or Docker Hub query was made)."
  },
  "lookup": {
    "pkg:docker/library/postgres": "postgresql"
  }
}

kev-products.json

CISA Known Exploited Vulnerabilities attributed to tracked products, one record per product with every attributed CVE.

URL
https://endoflife.ai/kev-products.json
Format
JSON, application/json
Refresh
Every build, against the KEV catalog version named in kev_catalog_version
Notice
_license key (CISA KEV line)
Top level
_license, _comment, generated, kev_catalog_version, source, product_count, products.
FieldTypeSourceMeaning
_commentstringMetadataHow attribution works. It ends: Absence from this file is not evidence of safety.
generatedstring (date)MetadataBuild date.
kev_catalog_versionstringCISA KEVThe version of CISA's catalog the attribution was derived from.
sourcestring (URL)MetadataOur methodology page for exploited vulnerabilities.
product_countintegerMetadataNumber of products in products.
products.<slug>.product_urlstring (URL)MetadataThe product page.
products.<slug>.cve_countintegerComputedCVEs in CISA's catalog attributed to the product.
products.<slug>.latest_addedstring (date)CISA KEVThe most recent date CISA added one of them.
products.<slug>.known_ransomware_usebooleanCISA KEVtrue when CISA marks known ransomware campaign use for at least one of them.
products.<slug>.forced_attributionbooleanCuratedtrue when the attribution was set by a human override recorded with a reason (for example where CISA files a product under component names), rather than by the name match alone.
entries[].cvestringCISA KEVCVE identifier. Entries are newest first.
entries[].date_addedstring (date)CISA KEVWhen CISA added it to the catalog.
entries[].due_datestring (date) or nullCISA KEVCISA's remediation due date for federal agencies.
entries[].required_actionstring or nullCISA KEVCISA's required-action text.
entries[].vulnerability_namestring or nullCISA KEVCISA's name for the vulnerability.
entries[].product_as_filedstringCISA KEV<vendorProject> / <product> as CISA filed it.
entries[].known_ransomware_usebooleanCISA KEVtrue when CISA lists known ransomware campaign use for this CVE.

Example record (entries cut to the first one)

{
  "apache-activemq": {
    "product_url": "https://endoflife.ai/apache-activemq",
    "cve_count": 3,
    "latest_added": "2026-04-16",
    "known_ransomware_use": true,
    "forced_attribution": false,
    "entries": [
      {
        "cve": "CVE-2026-34197",
        "date_added": "2026-04-16",
        "due_date": "2026-04-30",
        "required_action": "Apply mitigations per vendor instructions, follow applicable BOD 22-01 guidance for cloud services, or discontinue use of the product if mitigations are unavailable.",
        "vulnerability_name": "Apache ActiveMQ Improper Input Validation Vulnerability",
        "product_as_filed": "Apache / ActiveMQ",
        "known_ransomware_use": false
      }
    ]
  }
}

exploited-and-unpatchable.json

The Exploited & Unpatchable list: CVEs on CISA's catalog that affect product lines past end of life, where the fix ships only in supported lines. Each entry is curated and human-verified.

URL
https://endoflife.ai/exploited-and-unpatchable.json
Format
JSON, application/json
Refresh
Every build. Entries change when they are reviewed (last_reviewed); EPSS values are as of each entry's epss.scoreDate.
Notice
Its own license field (quoted above)
Top level
name, description, definition, methodology_url, license, signals, generated_at, entry_count, entries.
FieldTypeSourceMeaning
definitionstringMetadataThe inclusion rule, in full.
signals.cvss / .known_exploited / .epssstringMetadataWhat each of the three signals means. EPSS is model output, not human-verified, and never affects inclusion.
generated_atstring (timestamp)MetadataBuild time.
entry_countintegerMetadataNumber of entries.
entries[].cvestringCISA KEVCVE identifier.
entries[].productstringMetadataProduct slug.
entries[].product_namestringMetadataProduct display name.
entries[].vulnerabilitystringCuratedWhat the flaw is, in plain words.
entries[].weaknessstring or nullCuratedWeakness class (CWE), where stated.
entries[].cvssnumber or nullCurated from NVD or vendorCVSS base score, human-verified.
entries[].cvss_sourcestring or nullCuratedWho assigned that score (NVD, the CNA, the vendor advisory) and, where given, the vector.
entries[].epssobject or nullFIRST.org EPSSscore (probability of exploitation in the next 30 days, 0–1), percentile (0–1), scoreDate (the EPSS run date). Model output.
entries[].known_exploitedbooleanCISA KEVAlways true: presence on CISA's catalog is required for inclusion.
entries[].kev_date_addedstring (date)CISA KEVWhen CISA added the CVE.
entries[].kev_due_datestring (date) or nullCISA KEVCISA's due date.
entries[].affected_statementstring or nullCurated from vendorWhich versions the vendor says are affected.
entries[].fixed_versionsarray of stringCurated from vendorReleases that contain the fix.
entries[].affected_eol_versionsarray of stringCurated / LifecycleThe affected lines that are past end of life and receive no fix.
entries[].unpatchable_reasonstringCuratedWhy those lines will not be patched.
entries[].sourcesobject of URLsCuratedAlways kev, advisory, nvd and lifecycle (a lifecycle page: the vendor's, or ours); some entries add fix_release, exploitation or our_lifecycle.
entries[].last_reviewedstring (date)CuratedWhen endoflife.ai last reviewed the entry.

Example entry

{
  "cve": "CVE-2026-63077",
  "product": "teamcity",
  "product_name": "JetBrains TeamCity",
  "vulnerability": "Unauthenticated remote code execution via the agent polling protocol",
  "weakness": "Deserialization of untrusted data",
  "cvss": 9.8,
  "cvss_source": "JetBrains (CNA); NVD not yet scored",
  "epss": {
    "score": 0.8957,
    "percentile": 0.99788,
    "scoreDate": "2026-10-15"
  },
  "known_exploited": true,
  "kev_date_added": "2026-08-05",
  "kev_due_date": "2026-08-08",
  "affected_statement": "All releases prior to 2025.11.7, and 2026.1 through 2026.1.2",
  "fixed_versions": [
    "2025.11.7",
    "2026.1.3"
  ],
  "affected_eol_versions": [
    "2025.07",
    "2025.03",
    "2024.12",
    "2024.07",
    "2024.03",
    "2023.11",
    "2023.05",
    "2022.10",
    "2022.04"
  ],
  "unpatchable_reason": "JetBrains provides security fixes only for the current and immediately previous release lines. The fix exists solely in 2025.11.7 and 2026.1.3; every earlier line is past end of support and receives no update for this actively exploited flaw.",
  "sources": {
    "kev": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog",
    "advisory": "https://www.jetbrains.com/privacy-security/issues-fixed/",
    "nvd": "https://nvd.nist.gov/vuln/detail/CVE-2026-63077",
    "lifecycle": "https://endoflife.ai/teamcity"
  },
  "last_reviewed": "2026-08-07"
}

exploited-and-unpatchable.xml

The same entries as an RSS 2.0 feed, built from the same data in the same pass, newest review first. It leaves out EPSS and scores: each item carries only the two human-verified prose fields.

URL
https://endoflife.ai/exploited-and-unpatchable.xml
Format
RSS 2.0, application/xml
Refresh
Every build
ElementTypeSourceMeaning
item/titletextMetadata<CVE> — <product name> <affected end-of-life lines> (exploited, no patch coming).
item/linkURLMetadatahttps://endoflife.ai/exploited-and-unpatchable#<CVE>.
item/guidtextMetadataendoflife.ai:eu:<slug>:<CVE>, not a permalink.
item/pubDateRFC 822 dateCuratedThe entry's last_reviewed date (or kev_date_added if none).
item/descriptiontextCuratedvulnerability, then Why unpatchable: and unpatchable_reason.

Example item

    <item>
      <title>CVE-2026-63077 — JetBrains TeamCity 2025.07, 2025.03, 2024.12, 2024.07, 2024.03, 2023.11, 2023.05, 2022.10, 2022.04 (exploited, no patch coming)</title>
      <link>https://endoflife.ai/exploited-and-unpatchable#CVE-2026-63077</link>
      <guid isPermaLink="false">endoflife.ai:eu:teamcity:CVE-2026-63077</guid>
      <pubDate>Fri, 07 Aug 2026 00:00:00 GMT</pubDate>
      <description>Unauthenticated remote code execution via the agent polling protocol Why unpatchable: JetBrains provides security fixes only for the current and immediately previous release lines. The fix exists solely in 2025.11.7 and 2026.1.3; every earlier line is past end of support and receives no update for this actively exploited flaw.</description>
    </item>

eos-edge-devices.json

The EOS Edge Device feed: end-of-support status for edge platforms in the sense of CISA's BOD 26-02, with each platform's KEV record, CPE identifiers, sources and verification method. Schema version 2.0; cycle, eos and status are unchanged from schema 1.

URL
https://endoflife.ai/eos-edge-devices.json
Format
JSON, application/json
Refresh
Every build
Notice
_license key (endoflife.date and CISA KEV lines); feed.license points to the licence section quoted above
Top level
_license, _comment, schema_version, generated, source, bod_26_02, feed, kev_catalog_version, platform_count, lines_past_eos, lines_eos_within_12_months, recent_changes, platforms.
FieldTypeSourceMeaning
_commentstringMetadataA description of the feed, its statuses and its line fields.
schema_versionstringMetadata2.0.
generatedstring (date)MetadataBuild date.
source / bod_26_02string (URL)MetadataThe feed's page, and CISA's directive.
feed.json / .csv / .changelog / .licensestring (URL)MetadataThis file, the CSV, the change log, and the licence section.
kev_catalog_versionstringCISA KEVThe KEV catalog version used.
platform_countintegerMetadataNumber of platforms.
lines_past_eosintegerComputedLines with status eos, across all platforms.
lines_eos_within_12_monthsintegerComputedLines with status within-12-months.
recent_changes[]array of objectComputedThe newest 50 entries of the change log, same fields as eos-edge-changelog.json.
platforms[].slug / .namestringMetadataProduct slug and display name.
platforms[].categorystringCuratedDevice category (for example VPN gateway).
platforms[].product_urlstring (URL)MetadataThe product page.
platforms[].kevbooleanCISA KEVtrue when CVEs in CISA's catalog are attributed to the platform.
platforms[].kev_summaryobject or nullCISA KEVcve_count, latest_added, known_ransomware_use, catalog_version, as in kev-products.json. null when none.
platforms[].kev_entries[]array of objectCISA KEVEvery attributed CVE, newest first, with the same fields as entries[] in kev-products.json.
platforms[].cpearray of stringNIST CPE dictionaryIdentifiers from cpe-map.json; empty when unmapped.
platforms[].successorstring or nullCurated from vendorThe successor platform, where the vendor states one.
platforms[].sourcesarray of URLCuratedThe vendor pages the dates come from.
platforms[].verification.methodstringCuratedHow the dates are checked: manual (browser-verified), manual, webdates or webpost (an automated check that the vendor page still states the recorded dates) for our own catalog; upstream (endoflife.date) + vendor-overrides + daily verification sweep for products from endoflife.date.
platforms[].verification.note / .url / .last_verifiedstring or nullCuratedNotes on the check, the URL checked where one is recorded, and the last verification date where one is recorded.
lines[].cyclestringLifecycleThe release line.
lines[].labelstringLifecycleThe line's display label.
lines[].release_datestring (date) or nullLifecycleRelease date.
lines[].support_endstring (date) or nullLifecycleThe vendor's first milestone: end of engineering, end of development or last order, per the product page.
lines[].eosstring (date) or nullLifecycleEnd of support: the date the BOD status is based on. null when no date is published.
lines[].eol_finalstring (date) or nullLifecycleA later final end-of-life milestone, where the vendor publishes one.
lines[].statusstringComputedeos (past end of support, or declared ended with no date), within-12-months (BOD 26-02's horizon), scheduled (more than 12 months away) or no-date-announced.
lines[].days_to_eosinteger or nullComputedDays from the build date to eos; negative once passed.
lines[].eos_precisionstring, optionalLifecyclemonth or quarter when the vendor publishes the end of support to the month only; eos and days_to_eos then count to the month's last day (date precision). Absent = day precision. Not a CSV column.
lines[].lateststring or nullLifecycleLatest release on the line, where published.
lines[].ltsbooleanLifecycleWhether the line is marked long-term support in the lifecycle data.
lines[].linkstring (URL) or nullLifecycleThe source link for the line.

Example platform (kev_entries and lines cut to the first element)

{
  "slug": "ivanti-connect-secure",
  "name": "Ivanti Connect Secure",
  "category": "VPN gateway",
  "product_url": "https://endoflife.ai/ivanti-connect-secure",
  "kev": true,
  "kev_summary": {
    "cve_count": 15,
    "latest_added": "2026-10-13",
    "known_ransomware_use": true,
    "catalog_version": "2026.10.15"
  },
  "kev_entries": [
    {
      "cve": "CVE-2026-104935",
      "date_added": "2026-10-13",
      "due_date": "2026-10-16",
      "required_action": "Apply mitigations in accordance with vendor instructions, ensuring compliance with CISA’s BOD 26-04 Prioritizing Security Updates Based on Risk (see URL in Notes) guidance and CISA’s “Forensics Triage Requirements” (see URL in Notes). Follow applicable BOD 26-04 guidance for cloud services or discontinue use of the product if mitigations are unavailable. Stakeholders are responsible for evaluating each asset's internet exposure and ensuring adherence to BOD 26-04 patching guidelines.",
      "vulnerability_name": "Ivanti Connect Secure Authentication Bypass by Spoofing Vulnerability",
      "product_as_filed": "Ivanti / Connect Secure",
      "known_ransomware_use": false
    }
  ],
  "cpe": [
    "cpe:2.3:a:ivanti:connect_secure"
  ],
  "successor": null,
  "sources": [
    "https://hub.ivanti.com/s/article/Granular-Software-Release-EOL-Timelines-and-Support-Matrix",
    "https://hub.ivanti.com/s/article/Pulse-Connect-Secure-Software-Dates-Milestones"
  ],
  "verification": {
    "method": "manual (browser-verified)",
    "note": "manual — hub.ivanti.com is a Salesforce Lightning shell (browser only); re-verify article 000080084 (Granular EOL Matrix) + 000080079 (Pulse milestones); last verified 2026-10-09",
    "url": null,
    "last_verified": "2026-10-09"
  },
  "lines": [
    {
      "cycle": "25.1.3",
      "eos": "2027-04-30",
      "status": "within-12-months",
      "label": "25.1.3",
      "release_date": "2026-07-31",
      "support_end": null,
      "eol_final": null,
      "latest": "25.1.3",
      "lts": false,
      "days_to_eos": 195,
      "link": "https://hub.ivanti.com/s/article/Granular-Software-Release-EOL-Timelines-and-Support-Matrix"
    }
  ]
}

eos-edge-devices.csv

The same lines as eos-edge-devices.json, flattened to one row per platform line. The build fails if the JSON and the CSV disagree.

URL
https://endoflife.ai/eos-edge-devices.csv
Format
CSV, text/csv, UTF-8. Line 1 is the notice as a # comment; line 2 is the header. Empty cell = no value.
Refresh
Every build
ColumnTypeSourceMeaning
platform_slugstringMetadataplatforms[].slug.
platformstringMetadataplatforms[].name.
categorystringCuratedplatforms[].category.
cyclestringLifecyclelines[].cycle.
labelstringLifecyclelines[].label.
release_datedateLifecyclelines[].release_date.
support_enddateLifecyclelines[].support_end.
eosdateLifecyclelines[].eos.
eol_finaldateLifecyclelines[].eol_final.
statusstringComputedlines[].status.
days_to_eosintegerComputedlines[].days_to_eos.
platform_in_cisa_kevyes / noCISA KEVplatforms[].kev.
kev_cve_countintegerCISA KEVkev_summary.cve_count, or 0.
source_linkURLLifecyclelines[].link.
product_urlURLMetadataplatforms[].product_url.

Example (notice, header and first row)

# endoflife.ai eos-edge-devices.csv. Includes data from endoflife.date, MIT licence (https://github.com/endoflife-date/endoflife.date/blob/master/LICENSE). CISA Known Exploited Vulnerabilities catalog, public domain (https://www.cisa.gov/known-exploited-vulnerabilities-catalog). Terms: https://endoflife.ai/terms. Use of this file is subject to the terms at that URL. Licence scope: free for evaluation and for running, securing and planning your own IT estate, including through the official endoflife.ai integrations; a data licence from [email protected] is required to use endoflife.ai data to assess, rate, underwrite, audit, research or report on other organisations, in any product, service or deliverable for third parties, to redistribute or resell it, or to store it in bulk to build a dataset. Data that originates from endoflife.date stays under its MIT licence and CISA KEV records stay public domain; the licence requirement applies only to what endoflife.ai adds. Built 2026-10-16.
platform_slug,platform,category,cycle,label,release_date,support_end,eos,eol_final,status,days_to_eos,platform_in_cisa_kev,kev_cve_count,source_link,product_url
ivanti-connect-secure,Ivanti Connect Secure,VPN gateway,25.1.3,25.1.3,2026-07-31,,2027-04-30,,within-12-months,195,yes,15,https://hub.ivanti.com/s/article/Granular-Software-Release-EOL-Timelines-and-Support-Matrix,https://endoflife.ai/ivanti-connect-secure

eos-edge-changelog.json

Every change to an edge platform line between one published feed and the next, newest first. Each build compares its lines with the previously published feed and appends what changed; nothing is removed from the history.

URL
https://endoflife.ai/eos-edge-changelog.json
Format
JSON, application/json
Refresh
Every build
Top level
generated (build date), source (the feed page), entries.
FieldTypeSourceMeaning
entries[].datestring (date)MetadataThe build day the change was detected.
entries[].platformstringMetadataPlatform slug.
entries[].cyclestringLifecycleThe line that changed.
entries[].changestringComputedline-added, eos-date-changed, status-changed or line-removed.
entries[].from / .tostring or nullLifecycleFor status-changed: the old and new status. For the other kinds: the old and new end-of-support date (null = none).
entries[].statusstringComputedThe line's status after the change. On line-added and eos-date-changed entries.
entries[].eosstring (date) or nullLifecycleThe line's end-of-support date. On status-changed entries.

Example entry

{
  "date": "2026-10-13",
  "platform": "arista-eos",
  "cycle": "4.31",
  "change": "status-changed",
  "from": "within-12-months",
  "to": "eos",
  "eos": "2026-10-13"
}

verification.json

The machine-readable form of /accuracy: where every served date comes from, the vendor cross-check records, and every published correction applied over upstream data.

URL
https://endoflife.ai/verification.json
Format
JSON, application/json
Refresh
Every build
Notice
_license key (endoflife.date line)
Top level
_license, generated, data_freshness, provenance, scoreboard, description, counts, products, corrections.

Provenance classes

Every served lifecycle date is labelled at build time with one class. The file defines them in provenance.classes:

ClassMeaning (as published in the file)
vendor-overridevalue set from the vendor's own publication in data/vendor-overrides.json (source recorded)
vendor-fetchedan adapter read the vendor's own lifecycle page and it agrees with the served value
vendor-verifieda human cross-check against the vendor's documentation covers this cycle
customour own catalog, curated from the vendor (not in endoflife.date)
upstreamendoflife.date only
discrepancythe vendor page disagrees with the served value — under review

Confidence (provenance.confidence_rule): high when two or more independent source classes agree with the served value; medium with one source; low when there is an open discrepancy with the vendor feed, or a vendor feed not read for two weeks with nothing else agreeing.

Fields

FieldTypeSourceMeaning
generatedstring (date)MetadataBuild date.
data_freshness.builtstring (timestamp)MetadataBuild time.
data_freshness.modestringMetadatalive (endoflife.date read live during the build) or snapshot (built from the committed copy only).
data_freshness.upstream_live / upstream_snapshotintegerMetadataProducts read live from endoflife.date this build, and products served from the committed snapshot instead.
data_freshness.oldest_snapshot_days / snapshot_productsinteger / arrayMetadataAge in days of the oldest snapshot served, and which products were served from snapshot.
provenance.classes / confidence_rule / share_definitions / policyobject / stringMetadataDefinitions: the classes above, the confidence rule, what each share percentage counts, and the order of sources of record.
provenance.all_products / commercial_tierobjectComputedTotals for all products, and for the commercial tier (products with a paid-support market): products, dates, vendor_sourced, vendor_sourced_strict, catalog, discrepancies, confidence (high/medium/low) and the three share_*_pct percentages defined in share_definitions. The commercial tier adds products_list and products_missing.
provenance.commercial_tier_high_confidence_pctnumberComputedShare of commercial-tier dates at high confidence.
provenance.by_class.<class>integerComputedServed dates in each class, all products.
provenance.products.<slug>objectComputedThe same counts for one product: dates, vendor_sourced, vendor_sourced_strict, catalog, by_class, confidence, commercial_tier (boolean).
provenance.adapters.<slug>objectComputedOne per vendor feed we read directly: vendor, source (URL read), fetched_at (timestamp), age_days, stale (true when not read for more than 14 days), cycles (lines read), precision (day or month, the precision the vendor publishes).
provenance.discrepancies[]array of objectComputedOpen disagreements between a vendor feed and the served value: product, cycle, field, served, vendor, source.
provenance.upstream_contributionsobjectMetadataOur pull requests to endoflife.date: merged, open, total, generated, source (the GitHub search), and pulls[] (number, title, state, createdAt, mergedAt, url).
scoreboardobjectComputedThe figures on /accuracy#scoreboard: dates and vendor-sourced shares for all products and the commercial tier, the same share for the product pages with the most AI citations in the newest page-statistics export the build reads, live vendor feeds, open vendor disagreements, vendor sentinels checked, KEV products joined, edge platforms, CPE and purl coverage, and the last vendor drift caught. Its note lists the inputs.
descriptionstringMetadataWhat the file is.
counts.products_vendor_verifiedintegerComputedRecords with a completed check (status verified or corrected); matches the /accuracy statbar.
counts.published_correctionsintegerComputedPer-version correction rows in corrections.
products.<slug>.vendorstringCuratedThe vendor whose documentation was checked.
products.<slug>.sourceUrlstring (URL)CuratedThe vendor page checked.
products.<slug>.checkedAtstring (date)CuratedWhen it was checked.
products.<slug>.statusstringCuratedverified, corrected, discrepancy or unconfirmed.
products.<slug>.versionsCheckedarray of stringCuratedThe lines compared.
products.<slug>.notesstringCuratedWhat was compared and found.
products.<slug>.discrepancies[]array of objectCuratedWhere present: version, ours (served value), vendor (what the vendor states), detail.
corrections.<slug>.<cycle>objectLifecycle (vendor-override)A correction we serve over upstream data. Lifecycle fields set by the correction (eol, support, extendedSupport, releaseDate, latest, latestReleaseDate, lts and others) plus _source (the vendor publication), _reason, _appliedAt (date applied); _add marks a line that upstream does not list.

Example records: a vendor check, a correction, a product's provenance counts and a vendor feed

"products": {
  "microsoft-dynamics-sl": {
    "vendor": "Microsoft",
    "sourceUrl": "https://learn.microsoft.com/en-us/lifecycle/products/dynamics-sl-2018",
    "status": "verified",
    "versionsChecked": [
      "2018",
      "2015",
      "2011"
    ],
    "notes": "2026-10-09 lifecycle product pages for Dynamics SL 2011, 2015 and 2018 read twice, identical; Fixed policy, UTC stamps minus one day. SL 7.0 and older ended before 2021 and are not served.",
    "checkedAt": "2026-10-09"
  }
}

"corrections": {
  "ruby": {
    "3.2": {
      "eol": "2026-04-01",
      "_source": "https://www.ruby-lang.org/en/downloads/branches/",
      "_reason": "Official branch page lists 3.2 EOL 2026-04-01; upstream shows 2026-03-31.",
      "_appliedAt": "2026-07-18"
    }
  }
}

"provenance": { "products": {
  "postgresql": {
    "dates": 58,
    "vendor_sourced": 58,
    "vendor_sourced_strict": 58,
    "catalog": 0,
    "by_class": { "vendor-fetched": 58 },
    "confidence": { "high": 58, "medium": 0, "low": 0 },
    "commercial_tier": true
  }
} }

"provenance": { "adapters": {
  "almalinux": {
    "vendor": "AlmaLinux OS Foundation (release notes index)",
    "source": "https://wiki.almalinux.org/release-notes/",
    "fetched_at": "2026-10-12T13:57:45.372Z",
    "age_days": 3,
    "stale": false,
    "cycles": 3,
    "precision": "day"
  }
} }

changelog.json

Every correction, accepted upstream change and vendor override to the lifecycle data in the last 180 days, newest first: the same rows as /changelog.

URL
https://endoflife.ai/changelog.json
Format
JSON, application/json
Refresh
Every build. Holds the newest 300 rows of the 180-day window.
Top level
title, description, window_days, count, changes.
FieldTypeSourceMeaning
window_daysintegerMetadataThe window covered, in days (180).
countintegerMetadataRows in changes.
changes[].datestring (date)MetadataWhen the change was made.
changes[].kindstringCuratedCorrection (we corrected a value), Upstream change accepted (endoflife.date changed a value and we accepted the change), or Vendor override (a value we serve from the vendor's publication over the upstream one).
changes[].product / product_namestringMetadataProduct slug and display name.
changes[].cyclestring or nullLifecycleThe line changed; null when the change is not about one line.
changes[].fieldstringMetadataThe field key: eol, support, extendedSupport, releaseDate, latest, latestReleaseDate, lts, kev, kev_cves and others; * = the whole line (for example a line added).
changes[].field_labelstringMetadataThe field in words (end of life, active support end ...).
changes[].before / afterstringLifecycleThe value served before and after, as display text: a date, or words for non-dates (ended, day not published for true; no date published for false; not stated when empty). Long values are cut at 90 characters. For a vendor override, before is the value endoflife.date carries.
changes[].reasonstringCuratedWhy, in one sentence.
changes[].source_urlstring (URL) or nullCuratedThe source behind the change, when it is a URL.
changes[].sourcestring or nullCuratedThe source as text, when it is not just a URL.

Example row

{
  "date": "2026-10-15",
  "kind": "Vendor override",
  "product": "wordpress",
  "product_name": "WordPress",
  "cycle": "4.1",
  "field": "eol",
  "field_label": "end of life",
  "before": "2025-07-31",
  "after": "ended, day not published",
  "reason": "WordPress Security Team (John Blackbourn, 2025-06-18): will cease providing updates for WordPress versions 4.1 to 4.6 in July 2025.",
  "source_url": "https://make.wordpress.org/security/2025/06/18/security-updates-will-cease-for-wordpress-versions-4-1-through-4-6/",
  "source": null
}

changelog.xml

The same rows as changelog.json as an Atom feed, built from the same data in the same pass; the build fails if the two hold different numbers of entries.

URL
https://endoflife.ai/changelog.xml
Format
Atom (RFC 4287), application/atom+xml
Refresh
Every build
ElementTypeSourceMeaning
entry/titletextMetadata<product> <line>: <field_label> changed from <before> to <after>.
entry/idtag URIMetadatatag:endoflife.ai,<date>:changelog/<slug>/<cycle>/<field>.
entry/updatedtimestampMetadataThe change date at 12:00 UTC.
entry/linkURLMetadataThe product page.
entry/categorytermCuratedThe row's kind.
entry/summarytextCuratedkind, reason, then Source: and the source.

Example entry

  <entry>
    <title>WordPress 4.1: end of life changed from 2025-07-31 to ended, day not published</title>
    <id>tag:endoflife.ai,2026-10-15:changelog/wordpress/4.1/eol</id>
    <updated>2026-10-15T12:00:00Z</updated>
    <link href="https://endoflife.ai/wordpress"/>
    <category term="Vendor override"/>
    <summary>Vendor override. WordPress Security Team (John Blackbourn, 2025-06-18): will cease providing updates for WordPress versions 4.1 to 4.6 in July 2025. Source: https://make.wordpress.org/security/2025/06/18/security-updates-will-cease-for-wordpress-versions-4-1-through-4-6/</summary>
  </entry>

score-verify.json

The fixture our daily drift check uses to confirm the API returns exactly the scores the site computes: one representative version for every distinct combination of the four score factors present in the build, chosen alphabetically so the file stays stable.

URL
https://endoflife.ai/score-verify.json
Format
JSON, application/json
Refresh
Every build
Top level
generated, purpose, methodology, kev_catalog, combinations, versions_scored, samples, held.
FieldTypeSourceMeaning
generatedstring (date)MetadataBuild date.
purpose / methodologystringMetadataWhat the file is for; the score methodology page.
kev_catalog.generatedstring (date)CISA KEVWhen the KEV data used for scoring was last reviewed.
kev_catalog.named / ecosystemintegerComputedProducts named in CISA's catalog, and products whose core shipped component is.
kev_catalog.fingerprintstringComputedA short hash of the KEV product sets; the API must report the same one.
combinationsintegerComputedDistinct factor combinations, one sample each.
versions_scoredintegerComputedVersion-level scores computed in the build.
samples[].product / versionstringMetadataProduct slug and line.
samples[].score / gradeinteger / stringComputedThe version's EOL Risk Score and grade.
samples[].bandstringComputedThe score band shown on score cards: Low 0–25, Medium 26–50, High 51–75, Critical 76–100. Bands and grades use different cut points.
samples[].factorsobjectComputedeol_recency, attack_surface, cisa_kev_exposure, extended_support: the four factors for the version, the same names the API uses.
held[]array of objectLifecycleUpstream values this build refused to take and kept at the snapshot value until checked against the vendor: product, version, field (* = the line vanished upstream), value (served), upstream_now, why. Empty when nothing is held.

Example (samples cut to the first one)

{
  "generated": "2026-10-16",
  "purpose": "Drift check fixture: api.endoflife.ai must return these exact scores. See scripts/verify-api-scores.js.",
  "methodology": "https://endoflife.ai/risk-score",
  "kev_catalog": {
    "generated": "2026-10-16",
    "named": 169,
    "ecosystem": 3,
    "fingerprint": "fb7d896e"
  },
  "combinations": 86,
  "versions_scored": 12159,
  "samples": [
    {
      "product": "adobe-acrobat",
      "version": "10",
      "score": 70,
      "grade": "D",
      "band": "High",
      "factors": {
        "eol_recency": 40,
        "attack_surface": 10,
        "cisa_kev_exposure": 20,
        "extended_support": 0
      }
    }
  ],
  "held": []
}

SBOM enrichment files

Three static reference files describe what the API writes back onto a CycloneDX or SPDX document sent to POST https://api.endoflife.ai/v1/sbom?enrich=1. They change only when the enrichment schema changes.

URLs
/sbom-enrichment.json (the schema), /sbom-sample.cdx.json (a sample input), /sbom-sample.enriched.cdx.json (the same document after enrichment)
Format
JSON, application/json; the samples are CycloneDX JSON
Refresh
Static; the schema version is in version

sbom-enrichment.json is self-describing: in component_properties and document_properties each key is a property name in the endoflife: namespace and its value is that property's definition, so it is not repeated here.

FieldTypeSourceMeaning
_aboutstringMetadataHow properties are written for CycloneDX (component properties) and SPDX (annotations); everything else in the document is left as received.
namespace / versionstringMetadataThe property prefix (endoflife:) and the schema version.
resolution_rulestringMetadataHow a component is matched to a product: by purl only when it has one, by exact name otherwise unless strict, by exact model id for AI-BOM components; never a guess.
component_properties.<name>stringMetadataDefinition of each per-component property (status, product, cycle, eol_date, its source and verification date, risk score, KEV exposure ...).
document_properties.<name>stringMetadataDefinition of each document-level property (counts, the gate result, the purl map used).
metadata_toolsstringMetadataThe metadata.tools entry the API adds.
requestobjectMetadataThe endpoint, body, query parameters (enrich, strict, warn_days), response type and how many lines per request get risk scores by plan.
samples.input / .enrichedstring (URL)MetadataThe two sample files.
other_surfacesobjectMetadataOther tools that write the same schema (GitHub Action, Stack Scanner, MCP server, watch).
consistencystringMetadataWhich writer adds which properties. Only the API adds risk scores, KEV exposure and the date provenance fields.
ai_bomobjectMetadataHow AI model components are resolved: since, component_types, purl_types, matched_by_value, model_products, note.

Example (two of the component property definitions)

"endoflife:status": "active | warn | eol | not-tracked | version-not-tracked. 'warn' = end of life within the API's warning horizon; 'eol' = past end of life (or declared end-of-life with no date - see eol_date_published); 'version-not-tracked' = product known, this version not placeable (ranges such as ^18.3.1 land here).",
"endoflife:eol_date_source": "Where eol_date comes from, the same label the API serves: vendor-override, vendor-fetched, vendor-verified, custom, upstream or discrepancy (definitions in the OpenAPI document at https://api.endoflife.ai/openapi.json). API only."

The API

The API at https://api.endoflife.ai serves the same lifecycle data and version-level scores as these files, per product and version, for SBOMs and in batches. Its data dictionary is its OpenAPI 3.0 document, which defines every response field; plans and limits are on /api.

Each lifecycle answer carries a data_source field, defined in the OpenAPI document as:

Where the served eol_date was read from: the vendor page for a vendor-sourced date, https://endoflife.date for an upstream date, https://endoflife.ai/how-it-works for our own catalog.

The provenance class labels it uses are the ones defined under verification.json.

llms.txt and sitemaps

FileFormatRefreshWhat it holds
/llms.txtMarkdown, text/plainHand-written; the article list is regenerated every buildAn index of the site for AI assistants: what endoflife.ai is, the machine-readable files and API, methodology, guides and every article.
/sitemap.xmlSitemaps protocol XMLEvery buildEvery public page with lastmod, changefreq and priority.
/news-sitemap.xmlGoogle News sitemap XMLEvery buildRecently published articles.
/robots.txtRobots exclusion, text/plainStaticCrawler rules and the sitemap locations.

Something on this page does not match a file? Every field above was checked against the published file. If you find a difference, report it on /accuracy or write to [email protected].