# LLM Model Prices & Deprecation Change Tracker (`leadharbor/llm-model-prices-deprecation-tracker`) Actor

One table of LLM model ids, context windows, USD prices per million tokens and provider deprecation/shutdown dates, from models.dev and the OpenAI, Anthropic and Gemini deprecation pages. Reports what changed since your last run. Plain HTTP, pay per row.

- **URL**: https://apify.com/leadharbor/llm-model-prices-deprecation-tracker.md
- **Developed by:** [Mohammed Samaan](https://apify.com/leadharbor) (community)
- **Categories:** AI, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

Pay per event

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/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
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.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## 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.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## LLM Model Prices & Deprecation Change Tracker

Keeping a model list current by hand is slow: prices move, context windows change, and providers announce shutdown dates on separate pages. This actor reads four public sources, puts them into **one row schema**, and tells you **what changed since your last run**.

Use it to:

- see which models you depend on have a shutdown date, and what the provider recommends instead;
- compare input and output prices (USD per million tokens) and context windows across providers;
- run it on a schedule and get a change report: new or removed models, price changes, context changes, deprecation announced, retirement date changed.

Every row names its source URL and the time it was read. A field a source does not give is `null`. A retirement date is never inferred from a model name.

### Sources

| Source | Gives |
|---|---|
| [models.dev](https://models.dev) catalog (`api.json`) | provider-native model ids, context window, base prices, `deprecated`/`beta`/`alpha` status |
| OpenAI deprecations page | shutdown dates, announcement date (from the dated section heading), recommended replacement |
| Anthropic model deprecations page | state, deprecation date, retirement date or "not sooner than" note, replacement |
| Gemini API deprecations page | shutdown date, recommended replacement |

The OpenAI, Anthropic and Gemini data is read from those providers' **public documentation pages**. The actor requests each page **once per run**, after reading the site's `robots.txt` and stopping if it disallows the page, with an identifying User-Agent (`ApifyActor-llm-model-catalog-changes/0.1 (+https://apify.com/leadharbor/llm-model-prices-deprecation-tracker)`). It extracts facts only (model ids, dates, replacement ids) and copies no prose. You are responsible for how you use the output and for any terms that apply to you.

Rows for the same model id from models.dev and a deprecation page are merged into one row (`sources` lists both).

Attribution: the Gemini API deprecations page states its content is licensed under the Creative Commons Attribution 4.0 License; rows built from it carry that page's URL in `sourceUrls`. models.dev data is MIT licensed (see credits).

### Sample input

```json
{
  "providers": ["anthropic", "openai", "google"],
  "sources": ["models-dev", "openai-deprecations", "anthropic-deprecations", "google-deprecations"],
  "includeDeprecated": true,
  "compareToPrevious": true,
  "maxRows": 500
}
```

### Input

| Field | Type | Default | Meaning |
|---|---|---|---|
| `providers` | list of text | all | Provider keys to keep, lower case, as models.dev spells them (`anthropic`, `openai`, `google`, ...). An unknown name fails the run, names the field, suggests the closest match and lists the valid ones. Empty means every provider (thousands of rows, each billed). |
| `sources` | list | all four | `models-dev`, `openai-deprecations`, `anthropic-deprecations`, `google-deprecations`. |
| `includeDeprecated` | boolean | `true` | `false` drops rows whose status is `deprecated` or `retired`. |
| `sinceDate` | `YYYY-MM-DD` | none | Keep only rows with a deprecation or retirement date on or after it. |
| `compareToPrevious` | boolean | `true` | Store a snapshot and emit a change report on later runs. |
| `maxRows` | whole number 1-20000 | 1000 | Cap on model rows (and on billing). A fractional value is rejected. |

### Output

Four kinds of dataset record, told apart by `recordType`:

- `model-row`: one per model. `id` (raw provider id), `provider`, `name`, `status`, `contextTokens`, `inputPricePerMTokens`, `outputPricePerMTokens` (USD per 1,000,000 tokens, base price; context-size price tiers are not expanded), `deprecationDate`, `retirementDate`, `retirementNote`, `replacement`, `sources`, `sourceUrl`, `sourceUrls`, `observedAt`, `warnings`, `schemaVersion`.
- `source-status`: only when a source failed or looked incomplete. `status: "error"`, `kind` (`failed` or `suspect`), `source`, `sourceUrl`, `httpStatus`, `error` with the specific reason. Never billed.
- `change-report`: only when a diff exists. `changes[]` with `type` (`new`, `removed`, `price_changed`, `context_changed`, `deprecation_announced`, `retirement_date_changed`), `from`, `to`, plus `counts`.
- `run-summary`: always, last. `sourceStatus` for every source (status, HTTP code, rows, bytes, error), counts, warnings, whether the charge limit stopped the run. The same object is saved as `SUMMARY` in the default key-value store.

Real row from a run on 21 Oct 2026:

```json
{
  "recordType": "model-row",
  "schemaVersion": 1,
  "id": "gpt-4o-2024-08-06",
  "provider": "openai",
  "name": "GPT-4o (2024-08-06)",
  "status": "deprecated",
  "contextTokens": 128000,
  "inputPricePerMTokens": 2.5,
  "outputPricePerMTokens": 10,
  "deprecationDate": "2026-10-14",
  "retirementDate": "2027-01-14",
  "retirementNote": null,
  "replacement": "gpt-6-luna",
  "sources": ["models-dev", "openai-deprecations"],
  "sourceUrl": "https://models.dev/api.json",
  "sourceUrls": ["https://models.dev/api.json", "https://developers.openai.com/api/docs/deprecations"],
  "observedAt": "2026-10-21T10:57:59.729Z",
  "warnings": []
}
```

#### Date fields, exactly

- `deprecationDate`: the day the provider announced or marked the deprecation (the Anthropic "Deprecated" column, or the date in an OpenAI/Anthropic section heading such as `2026-10-14: ...`).
- `retirementDate`: the day the provider says the model stops working. Null when none is announced.
- `retirementNote`: Anthropic's "Not sooner than ..." statement for current models. It is a floor, not a commitment, so it is kept out of `retirementDate`.
- `replacement`: the replacement text as the page writes it. It is usually a model id, but a page can give prose or a footnote mark (for example `gpt-5 or gpt-4.1*`); it is not cleaned.

### Change report and safeguards

The first run with `compareToPrevious` only stores a baseline (named key-value store `llm-model-catalog-snapshots`, one baseline per combination of providers and sources). Later runs add one `change-report` record when something changed.

A source that answers 200 but with half a page does not cause false removals. A source is treated as `suspect` and ignored for that run when it returns fewer than 70 percent of its row count from the last run, or fewer rows than a complete answer has (models.dev 500, OpenAI 30, Anthropic 15, Gemini 30), and a page without its closing `</html>` counts as failed. Failed and suspect sources get a `source-status` row, contribute no model rows, are not billed, and their part of the snapshot is kept unchanged for the next comparison.

### Pricing

Pay per event:

| Event | Price | When |
|---|---|---|
| `model-row` | $0.0005 | per model row written |
| `change-report` | $0.01 | once per run, only when there is a diff |

`source-status` and `run-summary` records are free. A row is billed only after it is written, and only rows built from healthy sources exist. If every source fails the run fails and bills nothing. If your maximum charge per run is reached the run stops and says so in the status message and the summary.

Worked example: the sample input above returns 316 rows on 21 Oct 2026 = 316 x $0.0005 = $0.158 on the first run. A later run with 3 changes costs the rows ($0.158) plus $0.01 for the report. Set `maxRows` or narrow `providers` to spend less; `["anthropic"]` with `models-dev` and `anthropic-deprecations` is 39 rows.

### Use with AI agents / MCP

Send `{"providers": ["anthropic"], "sources": ["anthropic-deprecations", "models-dev"], "maxRows": 100}`. Read `recordType: "model-row"` records: `id`, `status`, `retirementDate`, `replacement`. To list upcoming shutdowns add `"sinceDate": "2026-10-21"`. Always read the last record (`run-summary`) and any `source-status` records before trusting an absence.

### FAQ

**Are the numbers exact?** They are copied from the sources and not converted. A price in a source can lag the provider's own price list; check the provider before committing spend.

**Why is a price `null`?** The source gave no number, or gave a non-number or negative value. The reason is in the row's `warnings`.

**Why do I see `warnings` about dates that differ between two tables?** The OpenAI page lists some ids under more than one dated section (for example `code-davinci-002`). The first, newest section wins and the warning shows the other value.

**Does it need a login, cookie or proxy?** No. Plain HTTPS GET of four public URLs plus two `robots.txt` files.

### Limits

- Prices are base prices. Models with context-size price tiers show the base tier only.
- Only the four listed sources are read. `retirementDate: null` means "no source here announced one", not "none exists".
- Provider page layouts change. When one does, that source reports `error` with "layout changed" and the others still deliver.
- While a source is failed or suspect, price and date changes for models that depend on it are not detected until it recovers.
- Models that appear only on a deprecation page have `null` prices and context.

### Tests

`npm test` runs the offline suite against responses recorded on 21 Oct 2026 (JSON entries and tables copied verbatim from the live sources into `test/fixtures/`, trimmed for size). `LIVE=1 node --test test/live.test.js` fetches the real sources, including their `robots.txt`.

### Open-source credits

| Package | Licence (from its LICENSE file) | Use |
|---|---|---|
| apify 3.7.2 (SDK) | Apache-2.0 | Actor runtime, dataset, key-value store, charging |
| caniuse-lite (transitive via apify) | CC-BY-4.0 | data package inside the SDK tree, not used by this code |
| map-stream (transitive) | MIT (`LICENCE` file; no `license` field in package.json) | SDK dependency |
| pause-stream (transitive) | dual MIT and Apache-2.0 (`LICENSE`) | SDK dependency |

All other production packages in the tree report MIT, ISC, BSD, 0BSD, Apache-2.0, BlueOak-1.0.0 or MIT/CC0 in their `package.json`. No GPL, AGPL or share-alike package is installed.

Data credits:

- models.dev: "MIT License, Copyright (c) 2025 models.dev" (github.com/sst/models.dev).
- Gemini API deprecations page, ai.google.dev/gemini-api/docs/deprecations: content licensed under Creative Commons Attribution 4.0.
- OpenAI and Anthropic deprecation pages: public documentation, read for facts only as described under Sources.

# Actor input Schema

## `providers` (type: `array`):

Provider names to keep, lower case, exactly as models.dev spells them (for example anthropic, openai, google, mistral, deepseek). An unknown name fails the run and lists the valid ones. Leave empty for every provider: that is thousands of rows, and each row is billed, so also set Max rows.

## `sources` (type: `array`):

Which sources to read. A source that cannot be read, looks truncated or looks incomplete is reported as a status-error row and in the run summary; the others still deliver. Each provider page is requested once per run, after its robots.txt.

## `includeDeprecated` (type: `boolean`):

Turn off to get only models no source marks as deprecated or retired.

## `sinceDate` (type: `string`):

Date as YYYY-MM-DD. When set, only rows that have a deprecation date or a retirement date on or after it are returned (use it to list upcoming shutdowns). Leave empty for all rows.

## `compareToPrevious` (type: `boolean`):

Keeps a snapshot in a named key-value store (llm-model-catalog-snapshots) and adds one change-report row when something changed: new or removed models, price changes, context changes, deprecation announced, retirement date changed. The first run only stores the baseline. Each combination of providers and sources has its own baseline.

## `maxRows` (type: `integer`):

Upper limit on model rows returned (and billed) per run.

## Actor input object example

```json
{
  "providers": [
    "anthropic",
    "openai",
    "google"
  ],
  "sources": [
    "models-dev",
    "openai-deprecations",
    "anthropic-deprecations",
    "google-deprecations"
  ],
  "includeDeprecated": true,
  "compareToPrevious": true,
  "maxRows": 500
}
```

# Actor output Schema

## `models` (type: `string`):

Dataset with model rows, the change report and the run summary.

## `summary` (type: `string`):

Source status, counts and warnings for this run.

# 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 = {
    "providers": [
        "anthropic",
        "openai",
        "google"
    ],
    "sources": [
        "models-dev",
        "openai-deprecations",
        "anthropic-deprecations",
        "google-deprecations"
    ],
    "maxRows": 500
};

// Run the Actor and wait for it to finish
const run = await client.actor("leadharbor/llm-model-prices-deprecation-tracker").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 = {
    "providers": [
        "anthropic",
        "openai",
        "google",
    ],
    "sources": [
        "models-dev",
        "openai-deprecations",
        "anthropic-deprecations",
        "google-deprecations",
    ],
    "maxRows": 500,
}

# Run the Actor and wait for it to finish
run = client.actor("leadharbor/llm-model-prices-deprecation-tracker").call(run_input=run_input)

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

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

```

## CLI example

```bash
echo '{
  "providers": [
    "anthropic",
    "openai",
    "google"
  ],
  "sources": [
    "models-dev",
    "openai-deprecations",
    "anthropic-deprecations",
    "google-deprecations"
  ],
  "maxRows": 500
}' |
apify call leadharbor/llm-model-prices-deprecation-tracker --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,leadharbor/llm-model-prices-deprecation-tracker"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/8t3dw8nrZk6hfqOa1/builds/xFCihMgYKjtjDewHT/openapi.json
