GET/v1/changes
What changed for a package, provider, model, repository or surface, newest first. Give none of them to read every change.
| Name | Type | Description |
|---|---|---|
package | string? | A package in any spelling: pkg:npm/react, npm:react, pypi:Django, or a bare name the catalog holds in one registry. A version after @ (npm:react@18.2.0) is the same as since. |
ecosystem | string? | The registry of a bare package name, for example npm or pypi. Only with package. |
provider | string? | A provider, for example anthropic or openai: its models, APIs and runtimes. |
model | string? | A model id or alias, for example claude-sonnet-4-5-20250929. |
repo | string? | A connected repository id from GET /v1/repos: the changes that reach it. Everything it depends on, the models, APIs and runtime release lines it runs on, and every change one of its impacts names. |
surface | string? | Is this still current? A model id, API host, runtime or package. The answer adds a surface block. |
id | string? | One change by its id, as a POST /v1/check finding names it (changeId, triggerIds): that change in full. An id no change has is a 404, not charged. |
reaches | boolean? | With repo: only the changes that reach its code, its open impacts and the end of life of the runtime lines it runs on, instead of every change to everything it depends on. |
kind | string? | Kinds, comma separated: removed, renamed, signature_changed, deprecated, behavior_changed, default_changed, config_changed, new_capability, security, end_of_life, retirement. |
severity | string? | Severities, comma separated: critical, high, medium, low, unknown. |
within | string? | Upcoming effective dates within this many days, from 1d to 3650d, for example 90d. This is the retirement calendar, soonest first. |
since | string? | A date (2026-01-31, 2026-01 or a full timestamp), a version of the package (18.2.0), or cutoff:<model> for the changes after that model's knowledge cutoff. |
format | string? | json (the default) or ics, an iCalendar file of every change in the window that has an effective date, up to 1,000. A calendar app cannot page, so limit and cursor page only the JSON form. Over MCP, where a client caps one result, the file holds up to limit. |
cursor | string? | The nextCursor of the previous page, with the same query. |
limit | number? | 1 to 200, default 50. |
Give at most one of package, provider, model, repo, surface and id; naming two is a 400. Every other parameter narrows the answer. Over MCP the same parameters are the whats_changed tool's arguments, and the comma lists may be arrays.
# What changed in a package after the version you run
curl "https://api.since.dev/v1/changes?package=npm:acme-sdk&since=3.2.0" \
-H "Authorization: Bearer $SINCE_DEV_KEY"
# Is this model still current?
curl "https://api.since.dev/v1/changes?surface=claude-sonnet-4-5-20250929" \
-H "Authorization: Bearer $SINCE_DEV_KEY"
# Everything retiring in the next 90 days across your repository's stack
curl "https://api.since.dev/v1/changes?repo=<repository-id>&within=90d" \
-H "Authorization: Bearer $SINCE_DEV_KEY"
# What happened after a model's training data ended
curl "https://api.since.dev/v1/changes?provider=anthropic&since=cutoff:claude-sonnet-4-5-20250929" \
-H "Authorization: Bearer $SINCE_DEV_KEY"Reading from a point
| since | reads |
|---|---|
2026-01-31 | Changes published, or published again, on or after that day (UTC), plus history by the date it happened: a change Since recorded from a source's first full list counts from its own latest date on or before the day Since recorded it, not from that day. A month (2026-01) or a full timestamp also works. |
18.2.0 | With a package: the changes that land in a later version, and the advisories that cover that version. The version must be in the catalog. The answer adds package.releasesBehind. |
cutoff:<model> | The changes after that model's knowledge cutoff: what happened since it was trained. A model Since does not track, or whose cutoff it does not know yet, is a 400. |
Is it still current: surface=
surface takes a model id or alias, an API host, a runtime or a package. It adds a surface block to the answer, with the status, the replacement, the dates and the source, and lists that surface's changes. A model on several platforms answers once for the provider's own platform and lists the others under platforms, because dates differ by platform. A surface Since does not track is unknown, with no source.
{
"kind": "model",
"key": "model:anthropic:claude-sonnet-4-5-20250929",
"provider": "anthropic",
"name": "Claude Sonnet 4.5",
"status": "deprecated",
"replacement": "claude-sonnet-5-5",
"dates": {
"retires": "2026-11-30"
},
"source": {
"url": "https://docs.example.com/models/deprecations",
"sources": [
"Anthropic"
]
},
"platforms": [
{
"platform": "anthropic",
"status": "deprecated",
"dates": {
"retires": "2026-11-30"
},
"replacement": "claude-sonnet-5-5"
},
{
"platform": "bedrock",
"status": "deprecated",
"dates": {
"notBefore": "2027-02-28"
},
"replacement": "claude-sonnet-5-5"
}
]
}The retirement calendar: within=
within=90d lists upcoming effective dates, soonest first. With no subject it covers everything Since.dev tracks. Add provider or repo for your own stack, and kind or severity to narrow it.
The response
{
"query": {
"package": {
"ecosystem": "npm",
"name": "acme-sdk",
"purl": "pkg:npm/acme-sdk",
"known": true
},
"since": {
"version": "3.2.0"
}
},
"package": {
"ecosystem": "npm",
"name": "acme-sdk",
"purl": "pkg:npm/acme-sdk",
"status": "active",
"latest": "4.1.0",
"since": "3.2.0",
"releasesBehind": 9
},
"changes": [
{
"id": "c0a8e2d6-41b7-4f3a-8a52-6d19b7e0f4c3",
"source": "catalog",
"surface": {
"kind": "package",
"key": "package:npm:acme-sdk",
"provider": "npm",
"name": "acme-sdk",
"ecosystem": "npm",
"purl": "pkg:npm/acme-sdk"
},
"kind": "removed",
"severity": "medium",
"title": "acme-sdk 4.0.0 removes Client.send",
"summary": "Client.send was removed. Client.request takes the same arguments.",
"replacement": "Client.request",
"platform": null,
"symbol": "Client.send",
"introducedIn": null,
"removedIn": "4.0.0",
"affectedRanges": [],
"effectiveDate": null,
"dates": {},
"evidence": [
{
"url": "https://github.com/acme/acme-sdk/releases/tag/v4.0.0",
"quote": "Removed: Client.send. Use Client.request.",
"sourceKind": "release_notes",
"license": null
}
],
"sources": [
"npm"
],
"detectedAt": "2026-10-12T15:02:31.000Z",
"occurredAt": null,
"publishedAt": "2026-10-12T15:02:32.000Z",
"changedAt": "2026-10-12T15:02:32.000Z"
}
],
"nextCursor": "WyJyZWNlbnQiLCIyMDI2LTEwLTEyVDE1OjAyOjMyLjAwMDAwMFoiLCJjMGE4ZTJkNi00MWI3LTRmM2EtOGE1Mi02ZDE5YjdlMGY0YzMiXQ",
"coverage": {
"sources": [
{
"name": "npm",
"health": "active",
"historyLoaded": true,
"lastSuccessAt": "2026-10-25T07:41:03.000Z",
"lastItemAt": "2026-10-25T07:39:48.000Z"
},
{
"name": "Open Source Vulnerabilities (OSV)",
"health": "active",
"historyLoaded": true,
"lastSuccessAt": "2026-10-25T07:30:00.000Z",
"lastItemAt": "2026-10-25T06:12:51.000Z"
}
],
"missing": []
}
}| Name | Type | Description |
|---|---|---|
query | object | How the input was read: the resolved package, provider, model or repository, and the since used. A package, provider or model the catalog does not track has known: false. |
surface | object? | With surface=: the lifecycle answer above. |
package | object? | With a package: ecosystem, name, purl, status, latest, and with a version since and releasesBehind, the stable releases after it. |
changes | array | The changes, below. |
nextCursor | string | null | Pass it back to read the next page. Null on the last page. |
coverage | object | Which sources the answer read and how fresh they are. Each source has its publisher's name, as a change's sources names it, health (active, degraded, failing or blocked), historyLoaded (false while the source's history is still loading), lastSuccessAt and lastItemAt. missing names sources the answer depends on that have never run, so their silence is not news. |
| Name | Type | Description |
|---|---|---|
id | string | Stable for one change. A change is published again with newer facts under the same id, so upsert by id and keep the latest copy. |
source | string | catalog for a structured, cited change. watch for a change on a source Since watches for your connected repositories; it has no kind and its severity is unknown. |
surface | object | kind, key, provider and name, and for a package the ecosystem and purl. The provider is the canonical one: api.openai.com is run by openai. |
kind | string | null | One of the kinds above. |
severity | string | One of the severities above. |
title | string | One line. |
summary | string | null | A short plain description. |
replacement | string | null | What to use instead, when the source names one. |
platform | string | null | For a model: the platform the dates apply to, such as bedrock. Null for the provider's own. |
symbol | string | null | The dotted path inside the package or API, such as Client.send. Null when the whole surface changed. |
introducedIn | string | null | The version that introduced it. |
removedIn | string | null | The version that removed it. |
affectedRanges | array | For an advisory: each range of affected versions, as introduced, fixed and lastAffected. |
effectiveDate | string | null | The effective date, YYYY-MM-DD. |
dates | object | Every date, YYYY-MM-DD, under what it means: retires, notBefore, deprecated, endOfLife and the others above. |
evidence | array | Each { url, quote, sourceKind, license }: the source, a short quote from it and, when the data must be attributed, its license (for example CC-BY-4.0). |
sources | array | The publishers that agree on this change, by name, such as Anthropic or endoflife.date. |
detectedAt | string | When Since first saw it. |
occurredAt | string | null | For history a source recorded rather than told: when it happened. Null otherwise. |
publishedAt | string | null | When Since published the change, or last published it again with newer facts. Null while it is only recorded, for example from a source's first full list. |
changedAt | string | What the newest-first order and since read: publishedAt, else for recorded history the date it happened, else detectedAt. |
Paging
Pages are keyset cursors. Pass nextCursor back unchanged with the same query to read the next page; it is null on the last one. New and updated changes both arrive at the front, never inside a page you hold. A cursor made for the newest-first order is refused on a within query, and the other way round, with a 400. A cursor pages one query and does not remember where you stopped.
Catching up, and changes that move
A change is published again when its kind, severity, dates, affected versions, introduced or removed version, withdrawal or known-exploited (CISA KEV) listing moves. It keeps its id and gets a newer publishedAt, so a moved retirement date or a raised severity reaches you as the same change at the front of the list. To pick up later, pass the newest changedAt you stored as since. It includes that instant, so the first changes may be ones you hold. Upsert each by id: replace your copy, and never drop a change because you have seen its id.
