Skip to main content
Planned. /v1/changes is specified here so you can design against it; it returns 404 until launch. Availability requires the incremental feature (Builder and Commercial). Follow the changelog for the release date.
The change feed lets you replay every change to the dataset after a cursor: new models, updated scores and rankings, pricing changes, and removals. Combined with a bulk snapshot, it replaces polling the live endpoints.
This is not /v1/updates. /v1/updates returns recently added models within a 1–30 day lookback, has no cursor, and does not report score, ranking, pricing, or removal changes. Keep using it for a “what’s new” list; use /v1/changes for synchronization.

Sync model

1

Seed from a snapshot

Import the latest snapshot and keep its change_cursor.
2

Pull changes

Call GET /v1/changes?cursor=<saved> and apply every event in order.
3

Advance the cursor

Persist next_cursor only after the page is applied. Repeat while has_more is true.
4

Repeat on a schedule

The feed has daily granularity, so a daily job is enough. Each page costs one data response.

Endpoint

Response

integer
required
Monotonically increasing sequence number across the whole feed. The cursor is the base64url encoding of the last seq you applied.
date
required
UTC day the change was published. The feed has daily granularity: several changes to one entity on the same day are collapsed into one event carrying the end-of-day state.
string
required
model, score, ranking, or pricing.
string
required
Stable identifier for the record. Composite for score (model_id:benchmark_id), ranking (model_id:category), and pricing (model_id:provider).
string
required
upsert or delete. There is no separate create: the first upsert for an entity_id is a create.
object | null
required
Full current record for upsert, using the same fields as the corresponding snapshot dataset. null for delete (a tombstone).
string
required
Pass as cursor on the next call. Present even when has_more is false, so you can resume later.
boolean
required
true when more events exist after this page. Keep paging until false.
date
required
Earliest day still in the feed. Cursors before it are expired.

Guarantees

Retention and cursor expiry

Events are retained for 13 months. If you pass a cursor older than retained_from, the API returns 410 with error.code: "cursor_expired". Recover by re-importing the latest snapshot and continuing from its change_cursor. A daily or weekly sync will never hit this; it exists to protect against jobs that were paused for a year. Omitting cursor starts from retained_from. That is 13 months of events — fine for a one-off, but a snapshot is cheaper for the initial load.

Schema changes

Event data follows the snapshot schema_version in effect on occurred_on. Minor versions add optional fields; ignore unknown keys. Major versions are announced in the changelog 30 days ahead, and the feed emits a synthetic upsert for every affected entity on the cutover day so that consumers on the new schema converge without a resync.

Filtering and quota

entity_type reduces the volume you pull but does not change cursors: seq values are global, so you can switch filters between calls without losing position. Each page is one data response regardless of limit, so always request limit=1000.

Full resynchronization

Resync from a snapshot when:
  • You receive cursor_expired.
  • Your local checksums disagree with the manifest for a dataset you rebuilt.
  • A major schema_version bump changes fields you cannot migrate in place.
A resync is the same five steps as the initial load and costs a handful of data responses.