Watchlist

Get Watchlist Price Updates (Cursor Delta Poll)

Price changes for your watched cards since your cursor.

How it works

  1. First call: omit cursor. You'll page through the current state of every watched item, oldest price-change first (bootstrap walk).
  2. Store next_cursor from each response and pass it to the next call.
  3. While has_more is true, loop immediately. Once it is false you are caught up — keep polling with the latest cursor (once a minute is plenty).

Update fields

  • event"changed" = new current price for this card+grade. "removed" = the price was retracted and nothing replaced it — drop your cached value (price fields are null).
  • card_id — CardHedge card ID.
  • provider / grade_label — grading company and grade (e.g. PSA / "PSA 10").
  • latest_price — current price for this card+grade.
  • median_price_90d / sample_count_90d — 90-day median and sale count, when available.
  • last_updated — time of the underlying sale.
  • changed_at — when this state change entered the feed (drives cursor ordering).

Semantics

  • Events are coalesced state changes: "the current price for this card+grade changed", not per-sale records. If a card trades three times between polls you see the latest state once.
  • Apply events in order; the same card+grade may legitimately appear more than once in a walk (old state then newer state). Last state wins.
  • Cursors never expire. A client offline for weeks just catches up.
  • Updates appear here ~2–3 minutes after a sale syncs: a short safety horizon guarantees no update can ever be skipped, trading a little latency for a lossless feed.
  • On errors, retry with the same cursor — no updates are lost. Cursors are opaque; only ever pass back a cursor this endpoint returned.

Politeness

Poll on a timer started at process launch (e.g. every 60s), not on the wall-clock minute — synchronized :00 polling creates load spikes.

Example: bootstrap, then poll with the cursor

# 1) First call: no cursor. Returns current state of your watchlist
#    plus a next_cursor. Repeat with the cursor while has_more is true.
curl -X POST -H "X-API-Key: your-api-key"          -H "Content-Type: application/json"          -d '{}'          "https://api.cardhedger.com/v1/cards/watchlist-updates"
# -> {"updates": [ ... ], "count": 24,
#     "next_cursor": "eyJ2IjoxLCJjIjpbIjIwMjYtMDctMDNUMTg6MDE6MTIuNDEwMjMzKzAwOjAwIiw0MjFdLCJkIjpbbnVsbCxudWxsXX0=",
#     "has_more": false}

# 2) Every later poll: send back the next_cursor you received, verbatim.
curl -X POST -H "X-API-Key: your-api-key"          -H "Content-Type: application/json"          -d '{"cursor": "eyJ2IjoxLCJjIjpbIjIwMjYtMDctMDNUMTg6MDE6MTIuNDEwMjMzKzAwOjAwIiw0MjFdLCJkIjpbbnVsbCxudWxsXX0="}'          "https://api.cardhedger.com/v1/cards/watchlist-updates"
# -> only what changed since that cursor; {"updates": [], "count": 0, ...}
#    when nothing did. Store next_cursor each time - it never expires.

Example polling loop

import requests, time

API_KEY = "your-api-key"
URL = "https://api.cardhedger.com/v1/cards/watchlist-updates"
HEADERS = {"X-API-Key": API_KEY, "Content-Type": "application/json"}

cursor = None  # bootstrap: walks your full watchlist state first
while True:
    body = {"cursor": cursor} if cursor else {}
    data = requests.post(URL, headers=HEADERS, json=body).json()
    for u in data["updates"]:
        if u["event"] == "changed":
            print(f'{u["card_id"]} {u["grade_label"]}: ${u["latest_price"]}')
        else:  # "removed"
            print(f'{u["card_id"]} {u["grade_label"]}: price retracted')
    cursor = data["next_cursor"]
    if not data["has_more"]:
        time.sleep(60)  # caught up - relax to steady polling
post/v1/cards/watchlist-updates

Request body

cursorstring nullable

Opaque cursor from a previous response. Omit for the initial bootstrap walk (delta from the beginning of time).

limitinteger

Response

Price updates for watched cards since the cursor

countinteger required
next_cursorstring required
has_moreboolean required

Changes

No recorded changes to this endpoint across all 2 revisions of this API.