Data dictionary
Contents
- How to read this page
- Licence and attribution
- scores.json
- checker-db.json
- scanner-db.json
- calendar/<product>.ics
- cpe-map.json
- purl-map.json
- kev-products.json
- exploited-and-unpatchable.json
- exploited-and-unpatchable.xml
- eos-edge-devices.json
- eos-edge-devices.csv
- eos-edge-changelog.json
- verification.json
- changelog.json
- changelog.xml
- score-verify.json
- SBOM enrichment files
- The API
- llms.txt and sitemaps
How to read this page
Where each value comes from
Every field row carries one of these labels:
- Lifecycle A lifecycle value (a date, a version, a support flag) exactly as the product pages and the API serve it. Each one comes from the vendor's own publication, from the endoflife.date community dataset, or from our own catalog curated from the vendor. Which of these applies to each date is published: per product in verification.json and on /accuracy, and per answer in the API's
data_sourcefield. The classes arevendor-override,vendor-fetched,vendor-verified,custom,upstreamanddiscrepancy, defined below. - CISA KEV Copied from CISA's Known Exploited Vulnerabilities catalog. Which tracked product a CVE belongs to is our attribution, derived from CISA's catalog by name with a guard against crediting another product's CVE; human overrides are recorded with reasons.
- Computed Calculated by endoflife.ai at build time from the data above: the EOL Risk Score™, statuses, counts and day counts. None of these are typed by hand.
- Curated Written and reviewed by endoflife.ai: reasons, notes, identifier decisions, and the Exploited & Unpatchable entries.
- Metadata Describes the file itself: build dates, URLs, versions, counts of records.
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
- Dates are ISO 8601 calendar dates (
YYYY-MM-DD) unless a field says it is a timestamp. - Some vendors publish an end date to the month only (for example "October 2026"). Such a date is still stored as
YYYY-MM-DD, the last day of that month, so it sorts and compares like any other date; a precision field next to it saysmonth(orquarter), and pages print the month, never the day or a day count. Vendors that state their own day rule ("through the last day of the month") keep day precision. Absent orday= the vendor names the day. The API carrieseol_date_precision,support_date_precisionandextended_support_date_precision, and adate_precision_notewhen one is notday. - Products are keyed by their slug, the same one used in page URLs:
https://endoflife.ai/<slug>. A release line is a cycle (for example PostgreSQL17), with a page athttps://endoflife.ai/<slug>/<cycle>. - An end-of-life value of
truemeans the line is past end of life but the vendor has not published the day;falsemeans it is not end of life and no date is published. - Absence from any of these files is not evidence that a product is safe or supported.
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:
- in JSON files whose top level is an object wrapping the data, it is the
_licensekey (scanner-db.json, cpe-map.json, purl-map.json, kev-products.json, eos-edge-devices.json, verification.json); - in scores.json and checker-db.json, whose top level is a map keyed by product, it is a sibling text file (scores.json.LICENSE.txt, checker-db.json.LICENSE.txt), so that no reader mistakes it for a product;
- in eos-edge-devices.csv it is the first line, a
#comment, before the column header.
The _license object has these fields:
| Field | Type | Source | Meaning |
|---|---|---|---|
| publisher | string | Metadata | Always endoflife.ai. |
| sources | array of string | Metadata | One sentence per third-party source present in the file: the endoflife.date line, the CISA KEV line, or both. |
| terms | string (URL) | Metadata | https://endoflife.ai/terms. |
| notice | string | Metadata | Always Use of this file is subject to the terms at that URL. |
| scope | string | Metadata | The data licence scope sentence, the same in every file and on the terms page (quoted below). |
| build_date | string (date) | Metadata | The 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| score | integer 0–100 | Computed | The product's EOL Risk Score: the sum of the four factors, capped at 100. Higher means more risk. |
| grade | string | Computed | Letter grade from the score: A 0–20, B 21–40, C 41–60, D 61–80, F 81–100. |
| label | string | Computed | The 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.recency | integer 0–40 | Computed | EOL recency points: how long ago the product's line reached end of life, or how soon it will. |
| factors.surface | integer 0–30 | Computed | Attack surface points, from the product's category tier. |
| factors.kev | integer 0–20 | Computed from CISA KEV | CISA KEV exposure points: 20 when the product is named in CISA's catalog, 10 when a core component it ships is, 0 otherwise. |
| factors.support | integer 0–10 | Computed | Extended support points: 10 when no extended support option is known for the product, 0 when one is. |
| eolDate | string (date) or null | Lifecycle | The 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). |
| eolDatePrecision | string, optional | Lifecycle | month or quarter when the vendor publishes eolDate to the month only; eolDate is then that month's last day (date precision). Absent = day precision. |
| nextEolDate | string (date) or null | Lifecycle | The 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. |
| nextEolDatePrecision | string, optional | Lifecycle | month or quarter when nextEolDate is published to the month only. Absent = day precision. |
| updatedAt | string (date) | Metadata | The 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| name | string | Metadata | The product's display name. |
| version | string | Computed | How many release lines are tracked, as text (29 major versions tracked). Not a version number. |
| status | string | Computed | eol 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. |
| eolDate | string (date) or null | Lifecycle | The next scheduled end-of-life date among lines still supported; if none, the most recent dated end of life. null when neither exists. |
| eolDatePrecision | string, optional | Lifecycle | month or quarter when the vendor publishes eolDate to the month only (date precision). Absent = day precision. |
| nextEolDate | string (date) or null | Lifecycle | The 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). |
| releaseDate | string (date) or null | Lifecycle | Release date of the oldest tracked line. Omitted when that line has no release date. |
| latestVersion | string | Lifecycle | The newest tracked line's latest release (for example 18.6), or the line's own name when no release number is published. |
| latestCycle | string | Lifecycle | The newest tracked line, as used in its page URL (/<slug>/<latestCycle>). |
| riskLevel | string | Computed | The first word of the product's score label in capitals: LOW, MODERATE, ELEVATED, HIGH or CRITICAL. Same score as scores.json. |
| cpe | array of string or null | NIST CPE dictionary | CPE 2.3 identifiers for the product, from cpe-map.json. null where no identifier represents it (the reason is in cpe-map.json). |
| purl | array of string or null | endoflife.date / registries / Curated | Package URLs for the product, from purl-map.json. null where none is known. |
| summary | string | Computed | One or two generated sentences: which lines are supported, how many are past end of life, and the next end of life. |
| recommendation | string | Computed | Upgrade to <name> <newest supported line>., or, when every line has ended, Migrate off <name> or obtain extended support. |
| patches | string | Computed | Which lines are actively supported, as text, or No versions receive security patches when every line has ended. |
| successor | null | Metadata | Reserved; 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| generated | string (date) | Metadata | Build date. |
| products.<slug>.name | string | Metadata | Display name. |
| products.<slug>.purl | array of string | endoflife.date / registries / Curated | Package URLs, from purl-map.json. Omitted when the product has none. |
| products.<slug>.cycles[] | array of object | Lifecycle | One entry per tracked release line, newest first. |
| cycles[].c | string | Lifecycle | The release line (cycle) name. |
| cycles[].eol | string (date) or boolean | Lifecycle | End-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[].p | string, optional | Lifecycle | month 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[].s | string | Computed | Status 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://).
| Property | Type | Source | Meaning |
|---|---|---|---|
| X-WR-CALNAME | text | Metadata | <name> lifecycle dates (endoflife.ai). |
| X-WR-CALDESC | text | Metadata | What the calendar holds, and the product page URL. |
| VEVENT UID | text | Metadata | <slug>-<cycle>-<field>@endoflife.ai, stable across builds, so a corrected date updates the existing event. <field> is support, eol or extendedSupport. |
| DTSTAMP | timestamp | Metadata | When the build wrote the file. |
| DTSTART / DTEND | date | Lifecycle | An all-day event on the lifecycle date (DTEND is the following day, per RFC 5545). |
| Month-only dates | text | Lifecycle | When 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). |
| SUMMARY | text | Metadata | <name> <line>: end of active support, : end of life or : end of extended support. |
| DESCRIPTION | text | Metadata | The same fact as a sentence with the date, and the version page URL, where the source of each date is shown. |
| URL | URI | Metadata | The version page, https://endoflife.ai/<slug>/<cycle>. |
| TRANSP | text | Metadata | Always 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| generated | string (date) | Metadata | When the mapping was last regenerated (not the build date). |
| dictionary | string (date) or null | Metadata | Date of the NIST CPE dictionary copy the identifiers were checked against. |
| about | string | Metadata | A description of the file. It states that every identifier was verified to exist and to be non-deprecated in the NIST CPE dictionary. |
| counts.verified | integer | Metadata | Products with a human-verified identifier. |
| counts.auto | integer | Metadata | Products with an automatic dictionary match. |
| counts.unmapped | integer | Metadata | Products recorded as having no identifier. |
| counts.undecided | integer | Metadata | Products with no decision. The pipeline refuses to publish while this is above 0. |
| products.<slug>.cpe | array of string or null | NIST CPE dictionary | CPE 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>.source | string | Curated / Computed | verified: 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>.basis | string | Curated | Why this identifier was chosen. Present when cpe is set. |
| products.<slug>.unmapped | boolean | Curated | true when no identifier represents the product. Present only then. |
| products.<slug>.reason | string | Curated | Why 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| generated | string (date) | Metadata | When the map was last harvested (not the build date). |
| about | string | Metadata | How to use the map, including the normalisation rule. |
| sources.upstream / .registry / .override | string | Metadata | Where 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_purl | integer | Metadata | Products with at least one purl. |
| counts.products_unmapped | integer | Metadata | Products recorded as having none, with a reason in unmapped. |
| counts.products_undisposed | integer | Metadata | Products with neither a purl nor a recorded reason. |
| counts.purls | integer | Metadata | Entries in lookup. |
| counts.by_type.<type> | integer | Metadata | Purls per package type (docker, npm, maven, rpm ...). |
| counts.upstream_products_fetched / upstream_errors | integer | Metadata | endoflife.date product records read by the harvest, and how many failed. |
| upstream_errors | array | Metadata | The products whose upstream read failed in the harvest. |
| unmapped.<slug> | string | Curated | Why the product has no purl. |
| undisposed | array of string | Metadata | Slugs with no decision yet. |
| products.<slug>.purl | array of string | endoflife.date / registries / Curated | The product's purls, without versions. |
| products.<slug>.sources.<purl> | string | Metadata | Where that purl came from: upstream, registry or override. |
| products.<slug>.basis | string | Curated | The recorded evidence for a purl decision. Present on every product with an override and on a few others. |
| lookup.<purl> | string | Computed | Normalised 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| _comment | string | Metadata | How attribution works. It ends: Absence from this file is not evidence of safety. |
| generated | string (date) | Metadata | Build date. |
| kev_catalog_version | string | CISA KEV | The version of CISA's catalog the attribution was derived from. |
| source | string (URL) | Metadata | Our methodology page for exploited vulnerabilities. |
| product_count | integer | Metadata | Number of products in products. |
| products.<slug>.product_url | string (URL) | Metadata | The product page. |
| products.<slug>.cve_count | integer | Computed | CVEs in CISA's catalog attributed to the product. |
| products.<slug>.latest_added | string (date) | CISA KEV | The most recent date CISA added one of them. |
| products.<slug>.known_ransomware_use | boolean | CISA KEV | true when CISA marks known ransomware campaign use for at least one of them. |
| products.<slug>.forced_attribution | boolean | Curated | true 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[].cve | string | CISA KEV | CVE identifier. Entries are newest first. |
| entries[].date_added | string (date) | CISA KEV | When CISA added it to the catalog. |
| entries[].due_date | string (date) or null | CISA KEV | CISA's remediation due date for federal agencies. |
| entries[].required_action | string or null | CISA KEV | CISA's required-action text. |
| entries[].vulnerability_name | string or null | CISA KEV | CISA's name for the vulnerability. |
| entries[].product_as_filed | string | CISA KEV | <vendorProject> / <product> as CISA filed it. |
| entries[].known_ransomware_use | boolean | CISA KEV | true 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| definition | string | Metadata | The inclusion rule, in full. |
| signals.cvss / .known_exploited / .epss | string | Metadata | What each of the three signals means. EPSS is model output, not human-verified, and never affects inclusion. |
| generated_at | string (timestamp) | Metadata | Build time. |
| entry_count | integer | Metadata | Number of entries. |
| entries[].cve | string | CISA KEV | CVE identifier. |
| entries[].product | string | Metadata | Product slug. |
| entries[].product_name | string | Metadata | Product display name. |
| entries[].vulnerability | string | Curated | What the flaw is, in plain words. |
| entries[].weakness | string or null | Curated | Weakness class (CWE), where stated. |
| entries[].cvss | number or null | Curated from NVD or vendor | CVSS base score, human-verified. |
| entries[].cvss_source | string or null | Curated | Who assigned that score (NVD, the CNA, the vendor advisory) and, where given, the vector. |
| entries[].epss | object or null | FIRST.org EPSS | score (probability of exploitation in the next 30 days, 0–1), percentile (0–1), scoreDate (the EPSS run date). Model output. |
| entries[].known_exploited | boolean | CISA KEV | Always true: presence on CISA's catalog is required for inclusion. |
| entries[].kev_date_added | string (date) | CISA KEV | When CISA added the CVE. |
| entries[].kev_due_date | string (date) or null | CISA KEV | CISA's due date. |
| entries[].affected_statement | string or null | Curated from vendor | Which versions the vendor says are affected. |
| entries[].fixed_versions | array of string | Curated from vendor | Releases that contain the fix. |
| entries[].affected_eol_versions | array of string | Curated / Lifecycle | The affected lines that are past end of life and receive no fix. |
| entries[].unpatchable_reason | string | Curated | Why those lines will not be patched. |
| entries[].sources | object of URLs | Curated | Always kev, advisory, nvd and lifecycle (a lifecycle page: the vendor's, or ours); some entries add fix_release, exploitation or our_lifecycle. |
| entries[].last_reviewed | string (date) | Curated | When 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.
| Element | Type | Source | Meaning |
|---|---|---|---|
| item/title | text | Metadata | <CVE> — <product name> <affected end-of-life lines> (exploited, no patch coming). |
| item/link | URL | Metadata | https://endoflife.ai/exploited-and-unpatchable#<CVE>. |
| item/guid | text | Metadata | endoflife.ai:eu:<slug>:<CVE>, not a permalink. |
| item/pubDate | RFC 822 date | Curated | The entry's last_reviewed date (or kev_date_added if none). |
| item/description | text | Curated | vulnerability, 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| _comment | string | Metadata | A description of the feed, its statuses and its line fields. |
| schema_version | string | Metadata | 2.0. |
| generated | string (date) | Metadata | Build date. |
| source / bod_26_02 | string (URL) | Metadata | The feed's page, and CISA's directive. |
| feed.json / .csv / .changelog / .license | string (URL) | Metadata | This file, the CSV, the change log, and the licence section. |
| kev_catalog_version | string | CISA KEV | The KEV catalog version used. |
| platform_count | integer | Metadata | Number of platforms. |
| lines_past_eos | integer | Computed | Lines with status eos, across all platforms. |
| lines_eos_within_12_months | integer | Computed | Lines with status within-12-months. |
| recent_changes[] | array of object | Computed | The newest 50 entries of the change log, same fields as eos-edge-changelog.json. |
| platforms[].slug / .name | string | Metadata | Product slug and display name. |
| platforms[].category | string | Curated | Device category (for example VPN gateway). |
| platforms[].product_url | string (URL) | Metadata | The product page. |
| platforms[].kev | boolean | CISA KEV | true when CVEs in CISA's catalog are attributed to the platform. |
| platforms[].kev_summary | object or null | CISA KEV | cve_count, latest_added, known_ransomware_use, catalog_version, as in kev-products.json. null when none. |
| platforms[].kev_entries[] | array of object | CISA KEV | Every attributed CVE, newest first, with the same fields as entries[] in kev-products.json. |
| platforms[].cpe | array of string | NIST CPE dictionary | Identifiers from cpe-map.json; empty when unmapped. |
| platforms[].successor | string or null | Curated from vendor | The successor platform, where the vendor states one. |
| platforms[].sources | array of URL | Curated | The vendor pages the dates come from. |
| platforms[].verification.method | string | Curated | How 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_verified | string or null | Curated | Notes on the check, the URL checked where one is recorded, and the last verification date where one is recorded. |
| lines[].cycle | string | Lifecycle | The release line. |
| lines[].label | string | Lifecycle | The line's display label. |
| lines[].release_date | string (date) or null | Lifecycle | Release date. |
| lines[].support_end | string (date) or null | Lifecycle | The vendor's first milestone: end of engineering, end of development or last order, per the product page. |
| lines[].eos | string (date) or null | Lifecycle | End of support: the date the BOD status is based on. null when no date is published. |
| lines[].eol_final | string (date) or null | Lifecycle | A later final end-of-life milestone, where the vendor publishes one. |
| lines[].status | string | Computed | eos (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_eos | integer or null | Computed | Days from the build date to eos; negative once passed. |
| lines[].eos_precision | string, optional | Lifecycle | month 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[].latest | string or null | Lifecycle | Latest release on the line, where published. |
| lines[].lts | boolean | Lifecycle | Whether the line is marked long-term support in the lifecycle data. |
| lines[].link | string (URL) or null | Lifecycle | The 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.
| Column | Type | Source | Meaning |
|---|---|---|---|
| platform_slug | string | Metadata | platforms[].slug. |
| platform | string | Metadata | platforms[].name. |
| category | string | Curated | platforms[].category. |
| cycle | string | Lifecycle | lines[].cycle. |
| label | string | Lifecycle | lines[].label. |
| release_date | date | Lifecycle | lines[].release_date. |
| support_end | date | Lifecycle | lines[].support_end. |
| eos | date | Lifecycle | lines[].eos. |
| eol_final | date | Lifecycle | lines[].eol_final. |
| status | string | Computed | lines[].status. |
| days_to_eos | integer | Computed | lines[].days_to_eos. |
| platform_in_cisa_kev | yes / no | CISA KEV | platforms[].kev. |
| kev_cve_count | integer | CISA KEV | kev_summary.cve_count, or 0. |
| source_link | URL | Lifecycle | lines[].link. |
| product_url | URL | Metadata | platforms[].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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| entries[].date | string (date) | Metadata | The build day the change was detected. |
| entries[].platform | string | Metadata | Platform slug. |
| entries[].cycle | string | Lifecycle | The line that changed. |
| entries[].change | string | Computed | line-added, eos-date-changed, status-changed or line-removed. |
| entries[].from / .to | string or null | Lifecycle | For status-changed: the old and new status. For the other kinds: the old and new end-of-support date (null = none). |
| entries[].status | string | Computed | The line's status after the change. On line-added and eos-date-changed entries. |
| entries[].eos | string (date) or null | Lifecycle | The 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.
Provenance classes
Every served lifecycle date is labelled at build time with one class. The file defines them in provenance.classes:
| Class | Meaning (as published in the file) |
|---|---|
| vendor-override | value set from the vendor's own publication in data/vendor-overrides.json (source recorded) |
| vendor-fetched | an adapter read the vendor's own lifecycle page and it agrees with the served value |
| vendor-verified | a human cross-check against the vendor's documentation covers this cycle |
| custom | our own catalog, curated from the vendor (not in endoflife.date) |
| upstream | endoflife.date only |
| discrepancy | the 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
| Field | Type | Source | Meaning |
|---|---|---|---|
| generated | string (date) | Metadata | Build date. |
| data_freshness.built | string (timestamp) | Metadata | Build time. |
| data_freshness.mode | string | Metadata | live (endoflife.date read live during the build) or snapshot (built from the committed copy only). |
| data_freshness.upstream_live / upstream_snapshot | integer | Metadata | Products read live from endoflife.date this build, and products served from the committed snapshot instead. |
| data_freshness.oldest_snapshot_days / snapshot_products | integer / array | Metadata | Age in days of the oldest snapshot served, and which products were served from snapshot. |
| provenance.classes / confidence_rule / share_definitions / policy | object / string | Metadata | Definitions: the classes above, the confidence rule, what each share percentage counts, and the order of sources of record. |
| provenance.all_products / commercial_tier | object | Computed | Totals 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_pct | number | Computed | Share of commercial-tier dates at high confidence. |
| provenance.by_class.<class> | integer | Computed | Served dates in each class, all products. |
| provenance.products.<slug> | object | Computed | The same counts for one product: dates, vendor_sourced, vendor_sourced_strict, catalog, by_class, confidence, commercial_tier (boolean). |
| provenance.adapters.<slug> | object | Computed | One 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 object | Computed | Open disagreements between a vendor feed and the served value: product, cycle, field, served, vendor, source. |
| provenance.upstream_contributions | object | Metadata | Our pull requests to endoflife.date: merged, open, total, generated, source (the GitHub search), and pulls[] (number, title, state, createdAt, mergedAt, url). |
| scoreboard | object | Computed | The 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. |
| description | string | Metadata | What the file is. |
| counts.products_vendor_verified | integer | Computed | Records with a completed check (status verified or corrected); matches the /accuracy statbar. |
| counts.published_corrections | integer | Computed | Per-version correction rows in corrections. |
| products.<slug>.vendor | string | Curated | The vendor whose documentation was checked. |
| products.<slug>.sourceUrl | string (URL) | Curated | The vendor page checked. |
| products.<slug>.checkedAt | string (date) | Curated | When it was checked. |
| products.<slug>.status | string | Curated | verified, corrected, discrepancy or unconfirmed. |
| products.<slug>.versionsChecked | array of string | Curated | The lines compared. |
| products.<slug>.notes | string | Curated | What was compared and found. |
| products.<slug>.discrepancies[] | array of object | Curated | Where present: version, ours (served value), vendor (what the vendor states), detail. |
| corrections.<slug>.<cycle> | object | Lifecycle (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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| window_days | integer | Metadata | The window covered, in days (180). |
| count | integer | Metadata | Rows in changes. |
| changes[].date | string (date) | Metadata | When the change was made. |
| changes[].kind | string | Curated | Correction (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_name | string | Metadata | Product slug and display name. |
| changes[].cycle | string or null | Lifecycle | The line changed; null when the change is not about one line. |
| changes[].field | string | Metadata | The field key: eol, support, extendedSupport, releaseDate, latest, latestReleaseDate, lts, kev, kev_cves and others; * = the whole line (for example a line added). |
| changes[].field_label | string | Metadata | The field in words (end of life, active support end ...). |
| changes[].before / after | string | Lifecycle | The 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[].reason | string | Curated | Why, in one sentence. |
| changes[].source_url | string (URL) or null | Curated | The source behind the change, when it is a URL. |
| changes[].source | string or null | Curated | The 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.
| Element | Type | Source | Meaning |
|---|---|---|---|
| entry/title | text | Metadata | <product> <line>: <field_label> changed from <before> to <after>. |
| entry/id | tag URI | Metadata | tag:endoflife.ai,<date>:changelog/<slug>/<cycle>/<field>. |
| entry/updated | timestamp | Metadata | The change date at 12:00 UTC. |
| entry/link | URL | Metadata | The product page. |
| entry/category | term | Curated | The row's kind. |
| entry/summary | text | Curated | kind, 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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| generated | string (date) | Metadata | Build date. |
| purpose / methodology | string | Metadata | What the file is for; the score methodology page. |
| kev_catalog.generated | string (date) | CISA KEV | When the KEV data used for scoring was last reviewed. |
| kev_catalog.named / ecosystem | integer | Computed | Products named in CISA's catalog, and products whose core shipped component is. |
| kev_catalog.fingerprint | string | Computed | A short hash of the KEV product sets; the API must report the same one. |
| combinations | integer | Computed | Distinct factor combinations, one sample each. |
| versions_scored | integer | Computed | Version-level scores computed in the build. |
| samples[].product / version | string | Metadata | Product slug and line. |
| samples[].score / grade | integer / string | Computed | The version's EOL Risk Score and grade. |
| samples[].band | string | Computed | The 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[].factors | object | Computed | eol_recency, attack_surface, cisa_kev_exposure, extended_support: the four factors for the version, the same names the API uses. |
| held[] | array of object | Lifecycle | Upstream 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.
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.
| Field | Type | Source | Meaning |
|---|---|---|---|
| _about | string | Metadata | How properties are written for CycloneDX (component properties) and SPDX (annotations); everything else in the document is left as received. |
| namespace / version | string | Metadata | The property prefix (endoflife:) and the schema version. |
| resolution_rule | string | Metadata | How 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> | string | Metadata | Definition of each per-component property (status, product, cycle, eol_date, its source and verification date, risk score, KEV exposure ...). |
| document_properties.<name> | string | Metadata | Definition of each document-level property (counts, the gate result, the purl map used). |
| metadata_tools | string | Metadata | The metadata.tools entry the API adds. |
| request | object | Metadata | The endpoint, body, query parameters (enrich, strict, warn_days), response type and how many lines per request get risk scores by plan. |
| samples.input / .enriched | string (URL) | Metadata | The two sample files. |
| other_surfaces | object | Metadata | Other tools that write the same schema (GitHub Action, Stack Scanner, MCP server, watch). |
| consistency | string | Metadata | Which writer adds which properties. Only the API adds risk scores, KEV exposure and the date provenance fields. |
| ai_bom | object | Metadata | How 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
| File | Format | Refresh | What it holds |
|---|---|---|---|
| /llms.txt | Markdown, text/plain | Hand-written; the article list is regenerated every build | An index of the site for AI assistants: what endoflife.ai is, the machine-readable files and API, methodology, guides and every article. |
| /sitemap.xml | Sitemaps protocol XML | Every build | Every public page with lastmod, changefreq and priority. |
| /news-sitemap.xml | Google News sitemap XML | Every build | Recently published articles. |
| /robots.txt | Robots exclusion, text/plain | Static | Crawler 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].