How to use it: 3 steps
- Make a free Apify accountApify is the cloud platform where our tools run. The free plan gives you $5 of usage every month, no credit card needed. You pay Apify, not us.
- Paste your input and press StartNo code needed: you fill in a form (the input). It already has a working example. For example:
nodejs 18in Software (product + version). Each start is one run. - Download the resultsWhen the run ends, the results are in a table (the dataset). Open Output, press Export and pick Excel or CSV. JSON and the API are there too.
This tool is an Apify Actor: a small program that runs on Apify. You can also run it on a schedule, for example every Monday. New to it? Read how it works, step by step and the 8 words you need first.
What it does
Software End-of-Life Checker checks your software versions against endoflife.date. Add product + version rows such as nodejs 18.20.4, python 3.8 or ubuntu 20.04, or a CSV. You get one row per version with the matched release cycle, the support phase on a date (active, security-only, extended, EOL), the end of active support, end of life (EOL) and end of extended support dates, LTS status, the latest patch and a near-EOL flag.
- Finds the product. It accepts endoflife.date names (
nodejs), their aliases and common spellings (node,postgres,golang,Node.js). An unknown name gives anunknown_productrow with suggestions. It is never guessed. - Finds the release cycle. A cycle (
18), a minor version (3.8) or a full patch (18.20.4,v18.20.4) all work. Codenames (focal) and release names (SQL Server2019) work too. If a version fits several cycles (for examplepython 3), you get anambiguous_versionrow with the candidates. - Computes the support phase on the as-of date (today by default) and the days left to each date.
- Explains the match in
matchExplanation, for exampleproduct "node" → nodejs (alias); version "18.20.4" → cycle 18 (prefix match).
Good for
- Find which of your runtimes, databases and operating systems are already EOL or end soon.
- Plan upgrades for next quarter with
asOfDate. - Add lifecycle dates to an SBOM, CMDB or asset list (CSV in, rows out).
- Run a regular check of a fixed list and keep only rows that need action (
onlyAttention).
Not a good fit
- Vulnerability (CVE) scanning or a security audit. The Actor reports published lifecycle dates only.
- Products that endoflife.date does not track. They get an honest
unknown_productrow. - Vendor contracts. Paid extended support (for example Ubuntu Pro or a commercial Node.js offer) is shown as
extended, but the Actor cannot know whether you have it.
Who uses it
IT and ops teams
Find runtimes, databases and systems that are already EOL.
Engineering managers
Plan upgrades for next quarter with a future check date.
Asset and CMDB owners
Add lifecycle dates to an asset list from a CSV.
Price
Pay per event: you pay for each result the tool saves, plus a small start fee for each run. The price includes Apify platform costs. Apify bills you, not us. Prices below are for the Apify free plan.
| What you pay for | Price | When |
|---|---|---|
| Start fee | $0.003 | Charged once per run. This tool uses 1 GB of memory or less by default; more memory costs more. |
| Checked version | $0.0019 (from $0.0016 on paid Apify plans) | One input row matched to an endoflife.date product and release cycle and saved to the Dataset with its support phase and dates. |
| Unmatched row | $0.0002 | One input row saved to the Dataset that could not be matched: unknown product, unknown or ambiguous version, or an invalid row. Source errors are not charged. |
Examples
- One version: about $0.0049.
- 1,000 versions: about $1.90.
- Every day 100 versions, for 30 days: about $5.79 a month.
Free to try: the Apify free plan gives you $5 of usage every month, no credit card needed. That is enough for about 2,600 versions.
Prices checked on 24 October 2026. See the current price on Apify.
Questions
Is it a vulnerability scanner?
No. It shows published lifecycle dates only. Use our OSV scanner for CVEs.
Why does Node.js 18 say extended?
Some vendors offer paid extended support. The tool cannot know if you have it.
What if my product is not found?
You get an unknown_product row. It only covers products on endoflife.date.
How many rows can I check?
Up to 500 unique rows per run.
How fresh is the data?
Each run reads endoflife.date live. Each row has sourceLastModified (when the source last changed the product; null when the run used the full catalog, which has no per-product time) and fetchedAt (our read time).
Can I check a future date?
Yes. Set asOfDate. Dates in the past work too. The isLts value and the phase are computed for that date.
Details
Step by step on Apify
- Open the Input tab. The form already has five versions:
nodejs 18,python 3.8,postgresql 12,ubuntu 20.04andangular 15. - Click Start.
- Open the Overview view in the Output tab. Check
supportPhase,eolFromanddaysToEol. - Replace the list with your own versions (up to 500 rows), or paste a CSV into CSV text.
Use cases
| Task | Input | Key fields |
|---|---|---|
| Find EOL software in an inventory | items or csvText | supportPhase, isEol, eolFrom, needsAttention |
| Plan upgrades before a date | items + asOfDate | supportPhase, daysToEol, nearEol |
| Keep patch levels current | items with full versions | latestPatch, isLatestPatch, latestPatchDate |
| Short action list for a team | items + onlyAttention | rows with needsAttention: true only |
Tips for better results
- Set a future as-of date to see what will be EOL next quarter.
- Turn on only attention rows to get a short action list.
- Common names like node or postgres work. Unknown names get suggestions.
Limits and good to know
- Up to 500 unique rows per run. A larger input is refused
- Up to 10 distinct products: one request per product, at most 4 at a time and at least 150 ms apart. More than 10: one request for the full endoflife.date catalog (about 3 MB) instead
- Internal time budget is 240 seconds. Rows not reached in time get
errorrows and the run isCAPPED - If endoflife.date fails for 2 products in a row, the Actor stops sending requests. Rows already tried get
source_errorrows; the rest are not written.RUN_SUMMARYcounts them, so run again later - The data is only as complete as endoflife.date. Some products have no EOL or extended support dates
Troubleshooting
| Symptom | Check | Action |
|---|---|---|
unknown_product for a real product | suggestions | Use the suggested endoflife.date name. If there is none, endoflife.date does not track the product. |
unknown_version | suggestions (recent cycles) | Check the version. Some products use other cycle names, for example windows-server 2019. |
ambiguous_version | suggestions | Give the minor version, e.g. python 3.12 instead of python 3. |
supportPhase: "unknown" | eolFrom, eoesFrom | The source has no date for this phase on your asOfDate. |
source_error rows or PARTIAL | RUN_SUMMARY.message | endoflife.date did not answer. Run again later. |
Run failed with INVALID_INPUT | the run status message | Typical causes: no version in any row, a wrong date format, more than 500 rows, a private csvUrl. |
For developers
Sample result
One result from a real test run, in JSON. Real values change from run to run. In Excel or CSV each field is a column.
{
"input": { "index": 4, "source": "items", "product": "ubuntu", "version": "20.04", "raw": "ubuntu 20.04" },
"status": "complete",
"matchStatus": "matched",
"matchExplanation": "product \"ubuntu\" → ubuntu; version \"20.04\" → cycle 20.04 (exact match)",
"needsAttention": true,
"product": "ubuntu",
"productLabel": "Ubuntu",
"productCategory": "os",
"cycle": "20.04",
"cycleLabel": "20.04 'Focal Fossa' (LTS)",
"codename": "Focal Fossa",
"releaseDate": "2020-04-23",
"isLts": true,
"eoasFrom": "2022-10-01",
"eolFrom": "2025-05-31",
"eoesFrom": "2030-04-23",
"hasExtendedSupport": true,
"isEol": true,
"supportPhase": "extended",
"daysToEol": -499,
"daysToEoes": 1289,
"nearEol": false,
"latestPatch": "20.04.6",
"latestPatchDate": "2023-03-23",
"isLatestPatch": null,
"asOfDate": "2026-10-12",
"sourceUrl": "https://endoflife.date/ubuntu",
"apiUrl": "https://endoflife.date/api/v1/products/ubuntu",
"fetchedAt": "2026-10-12T08:20:20.391Z"
}Use it from code
Actor ID: cliqtomedia/software-end-of-life-checker. You need your Apify API token from Apify Console → Settings → API & Integrations.
from apify_client import ApifyClient
client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("cliqtomedia/software-end-of-life-checker").call(run_input={
"items": [
"nodejs 18",
"python 3.8",
"postgresql 12",
"ubuntu 20.04",
"angular 15"
],
"nearEolDays": 180
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
print(item)curl -X POST "https://api.apify.com/v2/acts/cliqtomedia~software-end-of-life-checker/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
-H "Content-Type: application/json" \
-d '{"items": ["nodejs 18", "python 3.8", "postgresql 12", "ubuntu 20.04", "angular 15"], "nearEolDays": 180}'# pip install langchain-apify
# set APIFY_TOKEN in your environment
from langchain_apify import ApifyActorsTool
tool = ApifyActorsTool("cliqtomedia/software-end-of-life-checker")
result = tool.invoke({"run_input": {"items": ["nodejs 18", "python 3.8", "postgresql 12", "ubuntu 20.04", "angular 15"], "nearEolDays": 180}})
print(result)The cURL call waits for the results, up to 300 seconds. For longer runs, start a run (/runs) and get the results with a webhook or by checking the run. Apify API docs. LlamaIndex, MCP for AI agents and the JavaScript client: AI agents & MCP.
Data fields (36)
| Field | Type | Meaning |
|---|---|---|
input | object | Your row as given: index (1-based), source (items, csvText or csvUrl), product, version and the raw text. |
status | string | Row result. complete = product and cycle found; not_found = unknown product, unknown or ambiguous version; error = invalid row or endoflife.date failure (not the same as 'no data'). |
matchStatus | string | Detailed match outcome. |
matchExplanation | string | How your product and version were matched, e.g. product "node" → nodejs (alias); version "18.20.4" → cycle 18 (prefix match). |
needsAttention | boolean | true when the cycle is not in active support (security-only, extended, eol, unknown), is near EOL, or the row could not be checked. |
product | string | Canonical endoflife.date product slug, e.g. nodejs. |
productLabel | string | Product name, e.g. Node.js. |
productCategory | string | endoflife.date category, e.g. lang, framework, database, os. |
cycle | string | Matched release cycle, e.g. 18, 3.8, 20.04. |
cycleLabel | string | Cycle label from endoflife.date, e.g. 20.04 'Focal Fossa' (LTS). |
codename | string | Cycle codename, if any. |
releaseDate | string | First release date of the cycle (YYYY-MM-DD). |
isLts | boolean | Long-term support cycle on the as-of date (uses ltsFrom when known). |
ltsFrom | string | Date the cycle becomes or became LTS, if published. |
eoasFrom | string | End of active support (bug fixes); after it only security fixes. null = the product has no such phase or the date is unknown. |
eolFrom | string | End of standard support (end of life, no more security fixes). null = unknown, not 'supported forever'. |
eoesFrom | string | End of extended (usually paid) support, e.g. Ubuntu ESM. Extended support is not standard support. |
hasExtendedSupport | boolean | true when the cycle has an extended (paid) support phase. |
isEol | boolean | true when standard support (security fixes from the vendor's standard policy) has ended on the as-of date — also during paid extended support. null when unknown. |
supportPhase | string | Support phase on the as-of date: upcoming (not released yet), active, security-only, extended (standard EOL passed, paid extended support still running), eol, or unknown (dates not published). |
daysToEoas | integer | Days from the as-of date to end of active support; negative = already passed. |
daysToEol | integer | Days from the as-of date to EOL; negative = already passed. |
daysToEoes | integer | Days from the as-of date to end of extended support; negative = already passed. |
nearEol | boolean | true when the cycle is still supported and EOL is within nearEolDays; null when the EOL date is unknown. |
eolDateKnown | boolean | false when endoflife.date has no EOL date for this cycle. |
latestPatch | string | Latest release in this cycle, e.g. 18.20.8. |
latestPatchDate | string | Date of the latest release. |
latestPatchUrl | string | Release notes / changelog link of the latest release. |
isLatestPatch | boolean | true/false when you gave a full version comparable with latestPatch; null otherwise. |
asOfDate | string | Date the phase and day counts were computed for (YYYY-MM-DD). |
sourceUrl | string | endoflife.date page of the product. |
apiUrl | string | endoflife.date API endpoint used for the product. |
sourceLastModified | string | When endoflife.date last changed this product's data. |
fetchedAt | string | When this run fetched the product data. |
error | string | Short reason for not_found and error rows; null when complete. |
suggestions | array | Unknown product: close endoflife.date slugs. Unknown or ambiguous version: available cycles. |
Input settings (7)
The main fields of the input form. With the API you send them as JSON.
| Setting | Name in the API | Type | Example value |
|---|---|---|---|
| Software (product + version) | items | array | ["nodejs 18", "python 3.8", "postgresql 12", "ubuntu 20.04", "angular 15"] |
| CSV text | csvText | string | |
| CSV URL | csvUrl | string | |
| Near-EOL window (days) | nearEolDays | integer | 180 |
| As-of date | asOfDate | string | |
| Time zone | timeZone | string | "UTC" |
| Only rows needing attention | onlyAttention | boolean | false |
