---
title: "Keep a golf club database current with a nightly GET /changes job"
description: "Store the release header from your last full scan, ask GET /changes what each later release added, updated or removed, and refetch only those clubs."
summary: "GolfAPI.dev incremental sync. Every authenticated catalog response carries Golfapi-Catalog-Release (a bld_ id) and Golfapi-Catalog-Published. Store the release from a full scan, then call GET https://api.golfapi.dev/changes?after=<release>. It returns later releases oldest first, each with changes[resource].added, updated and removed club ids (clb_), only for resources the key can read. Refetch added and updated ids, delete removed ids (they return 410), store the newest release. Paginate with cursor=page.next, sending after again, while page.has_more; limit is at most 50. A 400 on after means rescan. Auth: Bearer API key."
url: "https://golfapi.dev/blog/sync-with-changes/"
published: "2026-11-02"
updated: "2026-11-12"
tags: ["How-to"]
---

# Keep a golf club database current with a nightly GET /changes job

Once you've stored the catalog, you don't have to rescan it to stay current. `GET /changes` lists the clubs each release added, updated or removed since the one you stored, and you refetch only those.

## Store the release with your scan

Every authenticated catalog response names the catalog release it was read from, in two headers:

```text
Golfapi-Catalog-Release: bld_01M77S54P0FCDDQVVAB2DCKPY5
Golfapi-Catalog-Published: 2026-11-12T11:45:14Z
```

Run your first full scan as described in [Catalog sync](https://golfapi.dev/docs/sync/). Save the `Golfapi-Catalog-Release` value with the documents. Read it from the first page and check that the last page names the same release. If a release was published during the scan, the two differ; scan again or start the change feed from the earlier one.

## Ask what changed

Send the stored release as `after`:

```sh
curl 'https://api.golfapi.dev/changes?after=bld_01M6F0VDZH8HGAJAG815AJAGQC&limit=2' \
  -H "Authorization: Bearer $GOLFDEV_API_KEY"
```

The response lists the releases published after that one, oldest first, with club ids per resource. Response on 12 November 2026, cut to two releases and fewer ids:

```json
{
  "after": "bld_01M6F0VDZH8HGAJAG815AJAGQC",
  "data": [
    {
      "release": "bld_01M6H19R5Z93JRF43G4X40QWMF",
      "published_at": "2026-11-03T15:44:33.739102Z",
      "changes": {
        "drivers": {
          "added": [],
          "updated": [
            "clb_01M403FZ82QC5AJR2VQ1G2ESYP",
            "clb_01M462H41MSVDT5PGWTT60ZE7B"
          ],
          "removed": []
        },
        "hybrids": {
          "added": [],
          "updated": [
            "clb_01M408GWX27G0EVF721ADKTAN3"
          ],
          "removed": []
        }
      }
    },
    {
      "release": "bld_01M6H29WQ7DJVV0Q3EGESHNY5N",
      "published_at": "2026-11-03T16:02:24.486920Z",
      "changes": {
        "fairway-woods": {
          "added": [],
          "updated": [
            "clb_01M3XEAXTQZ1PVN4AAZPTFSSQ6"
          ],
          "removed": []
        },
        "drivers": {
          "added": [],
          "updated": [
            "clb_01M5CN3MJZHAHSBVK1BAGR1P0N"
          ],
          "removed": []
        }
      }
    }
  ],
  "page": {
    "limit": 2,
    "has_more": true,
    "next": "cur_eyJ5IjowLCJp…"
  }
}
```

`/changes` lists only the club types your key can read. A key without fairway-woods access never sees fairway-wood ids.

In a chat, an assistant connected to the [MCP server](https://golfapi.dev/mcp) answers the same question with `get_changes`. Ask "What changed in the catalog since release bld_01M6F0VDZH8HGAJAG815AJAGQC?" to check a release by hand. That release is in the live catalog, so the prompt needs a live key on Hobby or Pro; a Free account's test key reads the sample's own releases.

## Apply each release

For each release in order:

- Fetch every id in `added` and `updated` by id and replace the stored document. If you store a view or a `fields` list, send the same one.
- Delete every id in `removed`. A request for a removed id returns `410`.
- After the last release, store its `release` as your new `after`.

Follow `page.next` as `cursor` until `page.has_more` is false. Send `after` with every page; `limit` is at most 50. An id that appears in several releases needs only one fetch.

```python
import os
import requests

API = "https://api.golfapi.dev"
H = {"Authorization": f"Bearer {os.environ['GOLFDEV_API_KEY']}"}

def sync(after, store):
    params = {"after": after, "limit": 50}
    newest = after
    while True:
        r = requests.get(f"{API}/changes", headers=H, params=params, timeout=30)
        if r.status_code == 400:
            return None  # the history does not cover this release: rescan
        r.raise_for_status()
        body = r.json()
        for release in body["data"]:
            for resource, c in release["changes"].items():
                for club_id in c["added"] + c["updated"]:
                    doc = requests.get(f"{API}/{resource}/{club_id}", headers=H, timeout=30)
                    doc.raise_for_status()
                    store.put(resource, club_id, doc.json())
                for club_id in c["removed"]:
                    store.delete(resource, club_id)
            newest = release["release"]
        if not body["page"]["has_more"]:
            return newest
        params = {"after": after, "limit": 50, "cursor": body["page"]["next"]}
```

## When to rescan

`/changes` answers from a stored change history. If your `after` is older than that history, or is not a release id, the request returns `400` with `param: after`, and the message names the oldest release it can answer from. Run a full scan and start again from its release.

Rescan also when you add a club type to your key, change the view you store, or change your `fields` list. `/changes` reports changes to documents, so it says nothing about clubs you never stored.

## How often to call it

A release is published when a signed data change merges. Several can land on one day and none on another. Calling `/changes` once a day keeps a store within a day of the live catalog, at one request when nothing changed. Each request counts toward your plan's monthly quota. The [changelog](https://golfapi.dev/changelog/) has a readable weekly note of the same changes. To handle a saved id that stops resolving, see [build a club comparison](https://golfapi.dev/blog/compare-clubs/).
