Real-time end-of-life dates and EOL Risk Scores™ for 500+ software products — Node.js, Python, PHP, Ubuntu, Kubernetes, and more. Power your observability stack, SBOM tool, CI/CD pipeline, or security platform. Free to evaluate; production keys from $89 a month, issued the minute you check out.
eol_date_source, eol_date_verified_at, eol_date_confidence; see below).{"products":[{"slug":"nodejs","version":"18"}]}machine-learning-model component, a pkg:generic or pkg:huggingface purl, or a component named by a model id (gpt-5, claude-sonnet-4-5-20250929, gemini-2.5-pro) resolves by exact model id to that model line and returns its retirement date; the response reports it under matched_by.model. Components that carry a package URL (pkg:npm/[email protected], pkg:maven/org.apache.logging.log4j/[email protected]) resolve only by purl — exact, against the published purl-map.json — and components without one resolve by name. Unmatched components are listed with a reason, never guessed; versions resolve to the release family, never the patch build. The response carries counts (components, matched by purl vs name, scored), a summary (past EOL, KEV-exposed) and the same Score objects as /v1/batch. Every product response also now carries its purl list next to cpe, and /v1/batch accepts {"purl": "..."} items.?enrich=1 and the response is your document back, unchanged except for lifecycle facts written onto every component: CycloneDX properties in the endoflife: namespace (status, product, cycle, eol_date, days_until_eol / days_past_eol, purl_matched, matched_by, extended_support_available, score_url; risk_score, risk_grade, kev_exposed on the scored subset; status=not-tracked with a reason otherwise), a document-level summary under metadata.properties (counts, unique products, the gate verdict, the purl-map date) and a metadata.tools entry; SPDX gets the same as annotations. Serial numbers and bom-refs are never touched, and re-enriching replaces our entries rather than stacking them. ?strict=1 makes resolution purl-only (no name matching at all); ?warn_days=N sets the gate's warning window (default 90). Property reference: sbom-enrichment.json · before/after sample: input → enriched. The Stack Scanner writes the same file in the browser (Download enriched SBOM) and the eol-check Action writes it in CI (sbom-output).POST /v1/sbom/watch with {"email": "...", "sbom": <document>} turns the resolved lines into a watch list on the alert system: a confirmation email first, then emails when a watched line comes inside 12 months of end of life, at 90/60/30/7 days, on the day it passes, and immediately when a CISA known-exploited CVE lands on a watched end-of-life line. Only the slug:cycle lines are stored, never the document; the list replaces the previous one for that email, 50 lines maximum. An SBOM is a snapshot; this is the part that keeps watching after the pipeline moves on.Every date the API returns carries its own provenance, so a downstream system can decide how much to trust it. The fields appear on the score, status, product, batch and SBOM responses.
eol_date_sourcevendor-fetched (read from the vendor's own lifecycle page or API, refreshed twice daily), vendor-override (a documented correction with a vendor source), vendor-verified (a human check against the vendor page), custom (our own catalog, no public dataset carries it), upstream (the community endoflife.date dataset), or discrepancy (the vendor and the served date disagree and the case is under review).eol_date_source_urleol_date_verified_ateol_date_statusok when the vendor feed was read within 14 days and agrees, fallback when the feed is older than 14 days and its last good value is served, mismatch when the vendor and the served date differ, human, upstream or custom when that record is the only source.eol_date_agreementeol_date_confidencehigh when two or more independent sources agree, medium with one source, low when sources disagree or the only source is a stale feed. Filter on this field to gate automated decisions.Coverage and the live disagreement list are published on the accuracy report. Nothing is guessed: a date with no source is served as upstream, never dressed up.
# Score for Node.js 18 curl https://api.endoflife.ai/v1/score/nodejs/18 # With Pro API key curl https://api.endoflife.ai/v1/score/nodejs/18 \ -H "X-API-Key: your_key_here" # Batch request curl -X POST https://api.endoflife.ai/v1/batch \ -H "Content-Type: application/json" \ -H "X-API-Key: your_key_here" \ -d '{"products":[ {"slug":"nodejs","version":"18"}, {"slug":"python","version":"3.8"}, {"slug":"ubuntu","version":"20.04"} ]}'
{
"product": "nodejs",
"version": "18",
"latest_release": "18.20.8",
"eol_date": "2025-04-30",
"eol_date_source": "vendor-fetched",
"eol_date_source_url": "https://github.com/nodejs/Release/blob/main/schedule.json",
"eol_date_verified_at": "2026-09-05",
"eol_date_status": "ok",
"eol_date_agreement": 3,
"eol_date_confidence": "high",
"status": "eol",
"days_past_eol": 383,
"score": 85,
"band": "Critical",
"factors": {
"eol_recency": 35,
"attack_surface": 30,
"cisa_kev_exposure": 20,
"extended_support": 0
},
"score_card_url": "https://endoflife.ai/score/nodejs/18",
"methodology_url": "https://endoflife.ai/risk-score"
}
// Check if a product version is EOL const res = await fetch( 'https://api.endoflife.ai/v1/status/nodejs/18', { headers: { 'X-API-Key': process.env.EOL_API_KEY } } ); const data = await res.json(); if (data.is_eol) { console.warn( `${data.product} ${data.version} is EOL`, `(${data.days_past_eol} days past EOL)` ); }
import requests # Get EOL Risk Score for Ubuntu 20.04 res = requests.get( "https://api.endoflife.ai/v1/score/ubuntu/20.04", headers={"X-API-Key": "your_key_here"} ) data = res.json() print(f"{data['product']} {data['version']}") print(f"Score: {data['score']} {data['band']}") print(f"EOL: {data['eol_date']}") # Score: 80 Critical # EOL: 2025-05-31
# Lifecycle status for Node.js 18 curl -s https://api.endoflife.ai/v1/status/nodejs/18 # One-liner gate: exit 1 if EOL curl -s https://api.endoflife.ai/v1/status/nodejs/18 \ | jq -e '.is_eol | not' > /dev/null
import sys import requests data = requests.get( "https://api.endoflife.ai/v1/status/python/3.8" ).json() if data["is_eol"]: print(f"{data['product']} {data['version']} is EOL " f"({data['days_past_eol']} days past EOL)") sys.exit(1)
jobs: eol-gate: runs-on: ubuntu-latest steps: - name: Fail build if runtime is EOL run: | IS_EOL=$(curl -s https://api.endoflife.ai/v1/status/nodejs/18 | jq -r '.is_eol') if [ "$IS_EOL" = "true" ]; then echo "nodejs 18 is end-of-life" exit 1 fi
eol_gate: stage: test image: alpine:latest script: - apk add --no-cache curl jq - IS_EOL=$(curl -s https://api.endoflife.ai/v1/status/nodejs/18 | jq -r '.is_eol') - if [ "$IS_EOL" = "true" ]; then echo "nodejs 18 is end-of-life"; exit 1; fi
stage('EOL gate') { steps { script { def status = readJSON text: sh( script: 'curl -s https://api.endoflife.ai/v1/status/nodejs/18', returnStdout: true ) if (status.is_eol) { error "nodejs 18 is end-of-life (EOL ${status.eol_date})" } } } }
150 requests/day for 30 days (three times the anonymous limit), emailed to you instantly. No credit card.
Unlimited requests and the full batch endpoint. $199/month — cancel any time. Your API key is delivered instantly after payment, no humans involved.