# Changes

> https://meetkeely.com/developers/api/changes · Updated 2026-11-06 · Keely, by Metrica Fit LLC

An app that keeps a copy of a person’s data polls `GET /v1/changes?since=<time>` and fetches what changed, or subscribes to [events](https://meetkeely.com/developers/webhooks) and is told. A morning’s numbers arrive when the person’s iPhone syncs the band and backs the night up, which is usually once in the morning and then as the day goes on; once an hour is plenty, and once a day is enough for most apps.

## GET /v1/changes

- **Scope:** any; only the kinds your scopes cover are listed
- **Query:** `since`, a UTC time, at most 90 days back; `limit`, up to 500

```bash Request
curl "https://meetkeely.com/api/v1/changes?since=2026-09-27T06:00:00Z" \
  -H "Authorization: Bearer ACCESS_TOKEN"
```

```json Response
{
  "since": "2026-09-27T06:00:00Z",
  "next": "2026-09-27T15:20:00Z",
  "has_more": false,
  "changes": [
    { "kind": "night", "key": "2026-09-27", "date": "2026-09-27", "updated_at": "2026-09-27T06:30:00Z" },
    { "kind": "workout", "key": "5f3c0e1d2b4a6978", "date": "2026-09-27", "updated_at": "2026-09-27T15:20:00Z" },
    { "kind": "day", "key": "2026-09-27", "date": "2026-09-27", "updated_at": "2026-09-27T15:20:00Z" }
  ]
}
```

- **`kind`** is `night`, `day`, `workout`, `journal_tag` or `journal_entry`, and **`key`** the date, the workout’s id, the tag’s key or the entry’s id. Only the kinds your scopes cover are listed: nights need `nights`, days `days`, workouts `workouts`, and the journal’s rows `journal`.
- **Order and paging.** Changes come ordered by `updated_at`, then `key`, at most 500 at a time (`limit`, up to 500). When `has_more` is true, call again with `since=next`. `next` is the newest `updated_at` seen, or your `since` when nothing changed.
- **Overlap and dedupe.** A row can be written in the second you read, so on each poll start 5 minutes before the `next` you stored, as the Keely app does, and dedupe by `kind` and `key`. Reading a row twice is harmless; a night, a day or a workout is replaced whole when it changes.
- **Removals.** A workout the person removed comes with `"removed": true`: delete your copy. A night or a day that no longer exists simply stops being listed; fetching it answers `404`.
- **The floor.** `since` can be at most 90 days back. For the first fill, read the days you need with [`GET /v1/days?from&to`](https://meetkeely.com/developers/api/days#get-v1-days), then poll from the time you started.

`/v1/changes` and [`/v1/events`](https://meetkeely.com/developers/api/events) answer different questions: changes are rows by their `updated_at`, at any age, for catching up; events are typed, buffered and kept 30 days, for reacting.
