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:
Golfapi-Catalog-Release: bld_01M77S54P0FCDDQVVAB2DCKPY5
Golfapi-Catalog-Published: 2026-11-12T11:45:14Z
Run your first full scan as described in Catalog 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:
curl 'https://api.golfapi.dev/changes?after=bld_01M6F0VDZH8HGAJAG815AJAGQC&limit=2' \
-H "Authorization: Bearer $GOLFDEV_API_KEY"
Run export GOLFDEV_API_KEY='sk_test_…' with your key in the same terminal first. Leave $GOLFDEV_API_KEY as written; the shell replaces it with your key. How to set your 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:
{
"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 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
addedandupdatedby id and replace the stored document. If you store a view or afieldslist, send the same one. - Delete every id in
removed. A request for a removed id returns410. - After the last release, store its
releaseas your newafter.
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.
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 has a readable weekly note of the same changes. To handle a saved id that stops resolving, see build a club comparison.


