# Software EOL & Support Monitor (`monema/software-eol-support-monitor`) Actor

Track documented end-of-active-support and end-of-life dates for a software watchlist. Classify lifecycle risk, compare persistent snapshots, and emit structured change records for scheduled operations.

- **URL**: https://apify.com/monema/software-eol-support-monitor.md
- **Developed by:** [Maarten Vreeburg](https://apify.com/monema) (community)
- **Categories:** Automation
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

$2.00 / 1,000 successful software lifecycle checks

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js/docs.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python/docs.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## Software EOL & Support Monitor

Track documented software lifecycle dates before an unsupported release becomes an emergency migration.

This Apify Actor checks an explicit watchlist of software products and release cycles against the public **endoflife.date API v1**, classifies upcoming or passed lifecycle dates, and compares the documented fields with a persistent prior snapshot. Schedule it daily or weekly and connect the dataset to your existing Apify automation, webhook, Slack, email, or data pipeline.

> **Unofficial:** this community Actor is not affiliated with, sponsored by, endorsed by, or maintained by endoflife.date or any software vendor.

### Who it is for

- platform and SRE teams maintaining runtimes, databases, operating systems, and infrastructure tools;
- MSPs tracking lifecycle risk across customer environments;
- IT asset and vulnerability teams that need a compact lifecycle feed;
- engineering managers planning upgrades before active support or maintenance ends.

### What one run does

For each exact product/cycle target, the Actor:

1. fetches the product once from the fixed public `https://endoflife.date/api/v1` origin;
2. finds the requested release cycle;
3. reports documented release, active-support, maintenance, LTS, EOL, and latest-version facts;
4. assigns a conservative `lifecycle_status` using your warning window;
5. compares the fields with the previous successful run in a named key-value store;
6. writes one structured dataset row per target.

The Actor does **not** inventory your systems, decide whether a system is compliant, or infer semantic-version ordering. If you provide `deployedVersion`, it only checks exact equality with the latest version documented by the source.

### Input

```json
{
  "monitorId": "production-platform-stack",
  "targets": [
    {
      "product": "python",
      "cycle": "3.13",
      "label": "Payments API runtime",
      "deployedVersion": "3.13.14"
    },
    {
      "product": "postgresql",
      "cycle": "16",
      "label": "Primary database"
    },
    {
      "product": "ubuntu",
      "cycle": "24.04",
      "label": "Application hosts"
    }
  ],
  "warningDays": 180,
  "timeoutSeconds": 30,
  "maxConcurrency": 3
}
````

| Field | Required | Meaning |
|---|---:|---|
| `monitorId` | yes | Stable ID that isolates this watchlist's persistent baseline. Use the same value on scheduled runs. |
| `targets` | yes | 1–100 unique product/cycle objects, with no more than 50 unique products. |
| `targets[].product` | yes | Exact lowercase product slug used by endoflife.date, such as `python`, `nodejs`, `postgresql`, or `ubuntu`. |
| `targets[].cycle` | yes | Exact release-cycle name, such as `3.13`, `22`, `16`, or `24.04`. |
| `targets[].label` | no | Internal environment, service, owner, or asset label. |
| `targets[].deployedVersion` | no | Exact deployed version for an equality-only comparison with the documented latest release. |
| `warningDays` | no | 0–730 days; defaults to 180. Dates inside this window are classified as approaching. |
| `timeoutSeconds` | no | Per-product request timeout from 5–60 seconds. |
| `maxConcurrency` | no | 1–5 concurrent product requests. A product referenced by several cycles is fetched once per run. |

Find product and cycle names on [endoflife.date](https://endoflife.date/) or through its [API documentation](https://endoflife.date/docs/api/v1/).

### Output

A successful target produces a row shaped like this (values change with the source):

```json
{
  "label": "Payments API runtime",
  "product": "python",
  "cycle": "3.13",
  "status": "unchanged",
  "lifecycle_status": "approaching_end_of_active_support",
  "checked_at": "2026-07-15T08:00:00Z",
  "release_date": "2024-10-07",
  "eoas_from": "2026-10-01",
  "eol_from": "2029-10-31",
  "days_to_end_of_active_support": 78,
  "days_to_end_of_life": 1204,
  "is_lts": false,
  "is_maintained": true,
  "deployed_version": "3.13.14",
  "latest_version": "3.13.14",
  "update_available": false,
  "changed_fields": [],
  "changes": [],
  "state_store_mode": "named_key_value_store",
  "charged_event": "eol_cycle_check",
  "charged_event_count": 1,
  "charge_limit_reached": false
}
```

#### Snapshot status

- `initial_snapshot` — first successful check for this `monitorId` + product + cycle;
- `unchanged` — documented lifecycle fields match the prior successful snapshot;
- `changed` — one or more documented fields changed; inspect `changed_fields` and `changes`;
- `unknown_product` — the source returned no such product;
- `unknown_cycle` — the product exists, but that exact cycle was not found;
- `fetch_error` — the bounded upstream request or response validation failed.

#### Lifecycle status

- `maintained`
- `approaching_end_of_active_support`
- `active_support_ended`
- `approaching_end_of_life`
- `end_of_life`
- `not_maintained`
- `lifecycle_unknown`

These are mechanical labels derived from source fields and dates. They are not compliance, security, procurement, or upgrade recommendations.

### Persistent state

The Actor stores successful snapshots in the explicit named key-value store:

```text
software-eol-support-monitor-state-v1
```

Keys are SHA-256 digests of `monitorId`, product, and cycle, so they are valid Apify storage keys and separate independent watchlists. Failed and unknown targets do not overwrite a prior successful snapshot.

### Billing behavior

The Actor emits the `eol_cycle_check` charge event only after it has fetched a valid product record, found the exact cycle, classified it, compared it, and stored the new snapshot. Unknown products, unknown cycles, input rejects, and fetch failures are not charged by this Actor event. It respects the run's Apify maximum-charge limit and stops before processing another paid target when the limit is reached. The current amount is shown on the live Apify Store listing.

### Safety and operational limits

- The network destination is fixed to the HTTPS `endoflife.date` API v1 origin; user-supplied URLs and request headers are not accepted.
- Cross-origin redirects are rejected.
- Responses are capped at 2 MB and 2,000 release records per product.
- Requests use bounded timeouts, concurrency, and retries; `429` and transient server responses receive short capped backoff.
- Inputs are bounded to 100 targets and 50 unique product requests per run.
- The Actor does not need credentials for the upstream API. Do not put secrets in labels or input fields.

### Data-source and affiliation notice

Lifecycle data is supplied by the community-maintained [endoflife.date](https://endoflife.date/) project and can be incomplete, delayed, corrected, or different from a vendor's terminology. Its documentation currently labels API v1 as Beta and warns that breaking changes can occur. Always verify material decisions against the relevant software vendor's current documentation and contract. This Actor is an independent, unofficial integration and is not affiliated with, endorsed by, or sponsored by endoflife.date or any monitored vendor.

The endoflife.date API specification identifies the project license as MIT. Source links and upstream timestamps are included in output where available.

### Local development

```bash
python -m venv .venv
. .venv/bin/activate
pip install -r requirements.txt
python smoke_test.py
npx --yes apify-cli validate-schema
apify run --purge
```

The deterministic smoke test runs the real Actor three times against a bounded local fixture and verifies `initial_snapshot → unchanged → changed` using persistent local Actor storage.

### Deployment

```bash
npx --yes apify-cli push
```

After deployment, run the default input once, inspect the non-empty dataset, then run it again with the same `monitorId` to verify the named-store baseline is reused before scheduling production checks.

# Actor input Schema

## `monitorId` (type: `string`):

Stable ID for this independent watchlist, such as production-platform-stack.

## `targets` (type: `array`):

Each target needs an endoflife.date product slug and exact cycle. Optional deployedVersion enables a simple exact comparison with the latest documented patch release.

## `warningDays` (type: `integer`):

Classify a documented support or EOL date inside this many days as approaching.

## `timeoutSeconds` (type: `integer`):

Network timeout for each unique endoflife.date product request.

## `maxConcurrency` (type: `integer`):

Bounded concurrency for unique product requests.

## Actor input object example

```json
{
  "monitorId": "qa-default",
  "targets": [
    {
      "product": "python",
      "cycle": "3.13",
      "label": "Python 3.13",
      "deployedVersion": "3.13.14"
    }
  ],
  "warningDays": 180,
  "timeoutSeconds": 30,
  "maxConcurrency": 3
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "targets": [
        {
            "product": "python",
            "cycle": "3.13",
            "label": "Python 3.13",
            "deployedVersion": "3.13.14"
        }
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("monema/software-eol-support-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "targets": [{
            "product": "python",
            "cycle": "3.13",
            "label": "Python 3.13",
            "deployedVersion": "3.13.14",
        }] }

# Run the Actor and wait for it to finish
run = client.actor("monema/software-eol-support-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "targets": [
    {
      "product": "python",
      "cycle": "3.13",
      "label": "Python 3.13",
      "deployedVersion": "3.13.14"
    }
  ]
}' |
apify call monema/software-eol-support-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=monema/software-eol-support-monitor",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "Software EOL & Support Monitor",
        "description": "Track documented end-of-active-support and end-of-life dates for a software watchlist. Classify lifecycle risk, compare persistent snapshots, and emit structured change records for scheduled operations.",
        "version": "0.1",
        "x-build-id": "6VaW96iLDgFWZ7pCW"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/monema~software-eol-support-monitor/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-monema-software-eol-support-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/monema~software-eol-support-monitor/runs": {
            "post": {
                "operationId": "runs-sync-monema-software-eol-support-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/monema~software-eol-support-monitor/run-sync": {
            "post": {
                "operationId": "run-sync-monema-software-eol-support-monitor",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "monitorId",
                    "targets"
                ],
                "properties": {
                    "monitorId": {
                        "title": "Monitor ID",
                        "minLength": 1,
                        "maxLength": 200,
                        "type": "string",
                        "description": "Stable ID for this independent watchlist, such as production-platform-stack.",
                        "default": "qa-default"
                    },
                    "targets": {
                        "title": "Software cycles to monitor",
                        "minItems": 1,
                        "maxItems": 100,
                        "type": "array",
                        "description": "Each target needs an endoflife.date product slug and exact cycle. Optional deployedVersion enables a simple exact comparison with the latest documented patch release.",
                        "items": {
                            "type": "object",
                            "properties": {
                                "product": {
                                    "title": "Product slug",
                                    "type": "string",
                                    "description": "Exact endoflife.date product slug, for example python, nodejs, ubuntu, or postgresql.",
                                    "minLength": 1,
                                    "maxLength": 128
                                },
                                "cycle": {
                                    "title": "Release cycle",
                                    "type": "string",
                                    "description": "Exact release cycle, for example 3.13, 22, 24.04, or 17.",
                                    "minLength": 1,
                                    "maxLength": 100
                                },
                                "label": {
                                    "title": "Internal label",
                                    "type": "string",
                                    "description": "Optional label for your environment or owner.",
                                    "maxLength": 200
                                },
                                "deployedVersion": {
                                    "title": "Deployed version",
                                    "type": "string",
                                    "description": "Optional exact deployed version. The Actor only compares it for equality with the latest documented release; it does not infer semantic-version ordering.",
                                    "maxLength": 100
                                }
                            },
                            "required": [
                                "product",
                                "cycle"
                            ]
                        },
                        "default": [
                            {
                                "product": "python",
                                "cycle": "3.13",
                                "label": "Python 3.13",
                                "deployedVersion": "3.13.14"
                            }
                        ]
                    },
                    "warningDays": {
                        "title": "Warning window (days)",
                        "minimum": 0,
                        "maximum": 730,
                        "type": "integer",
                        "description": "Classify a documented support or EOL date inside this many days as approaching.",
                        "default": 180
                    },
                    "timeoutSeconds": {
                        "title": "Request timeout (seconds)",
                        "minimum": 5,
                        "maximum": 60,
                        "type": "integer",
                        "description": "Network timeout for each unique endoflife.date product request.",
                        "default": 30
                    },
                    "maxConcurrency": {
                        "title": "Maximum concurrent requests",
                        "minimum": 1,
                        "maximum": 5,
                        "type": "integer",
                        "description": "Bounded concurrency for unique product requests.",
                        "default": 3
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
