Summary
Both gamma-api.polymarket.com/markets/keyset and gamma-api.polymarket.com/events/keyset return HTTP 200 with a populated next_cursor, but passing that next_cursor back as cursor=... on the next request returns the same first-page items. The cursor parameter appears to be ignored server-side.
This blocks every client preparing for the May 1 2026 sunset of the legacy /markets, /events list endpoints, since the keyset endpoints are the documented replacement.
Reproducer
# Page 1, no cursor.
curl -s "https://gamma-api.polymarket.com/markets/keyset?limit=2" \
| python -c "import json,sys; d=json.load(sys.stdin); print('ids:', [m['id'] for m in d['markets']]); print('next_cursor:', d['next_cursor'][:40] + '...')"
# Page 2, using the next_cursor from page 1.
NXT=$(curl -s "https://gamma-api.polymarket.com/markets/keyset?limit=2" \
| python -c "import json,sys; print(json.load(sys.stdin)['next_cursor'])")
curl -s "https://gamma-api.polymarket.com/markets/keyset?limit=2&cursor=${NXT}" \
| python -c "import json,sys; d=json.load(sys.stdin); print('ids:', [m['id'] for m in d['markets']]); print('next_cursor:', d['next_cursor'][:40] + '...')"
Expected: page 2 returns ids strictly different from page 1 (advancing the keyset).
Observed: page 2 returns the identical ids and the identical next_cursor.
Same behavior on /events/keyset. Reproducible from any IP at the time of writing (2026-04-27).
What I've ruled out
- URL encoding of the cursor (with and without
urlencode)
- Cursor parameter name (
cursor, next_cursor, after all behave identically)
- Transport (POST body and
X-Cursor header don't help)
- Cloudflare cache (cache-buster
_=$RANDOM produces cf-cache-status: MISS but the server still returns the same first page)
- Filter combinations (with and without
closed=false, order=id, ascending=false — all loop)
- Decoding the cursor confirms it correctly encodes the last-seen id, e.g.:
{"v":1,"k":"markets","oh":"...","keys":[{"t":"string","v":"540817"}]}
so the server is correctly producing the cursor — it just isn't honoring the cursor on the next request.
A multi-page walk against /markets/keyset exhausts at 200 pages, returning ~1,000 unique markets and ~199,000 duplicates (every page is page 1).
Cross-check: CLOB cursor works
For comparison, clob.polymarket.com/markets cursor pagination works correctly:
NXT=$(curl -s "https://clob.polymarket.com/markets" | python -c "import json,sys; print(json.load(sys.stdin)['next_cursor'])")
# NXT = "MTAwMA==" (decodes to "1000")
curl -s "https://clob.polymarket.com/markets?next_cursor=${NXT}" | python -c "import json,sys; print(json.load(sys.stdin)['next_cursor'])"
# Returns "MjAwMA==" (decodes to "2000") — cursor advances correctly.
So this is gamma-keyset-specific; not a general issue with cursor handling at Polymarket.
Asks
- Is this a known issue? I haven't found a tracking item.
- ETA on a fix relative to the May 1 2026 sunset of
/markets and /events? If the cursor stays broken when legacy sunsets, every keyset client loses pagination simultaneously.
- Any documented workaround on the keyset endpoints? (Alternative pagination parameter name, or a documented requirement I've missed.)
- If no near-term fix is planned, is
clob.polymarket.com/markets (whose cursor works) a supported alternative source for the full market universe? It returns a different shape so clients would need to map.
Happy to provide more diagnostic data — full request/response captures, cursor base64 decodes, etc. — if useful.
Summary
Both
gamma-api.polymarket.com/markets/keysetandgamma-api.polymarket.com/events/keysetreturn HTTP 200 with a populatednext_cursor, but passing thatnext_cursorback ascursor=...on the next request returns the same first-page items. The cursor parameter appears to be ignored server-side.This blocks every client preparing for the May 1 2026 sunset of the legacy
/markets,/eventslist endpoints, since the keyset endpoints are the documented replacement.Reproducer
Expected: page 2 returns ids strictly different from page 1 (advancing the keyset).
Observed: page 2 returns the identical ids and the identical
next_cursor.Same behavior on
/events/keyset. Reproducible from any IP at the time of writing (2026-04-27).What I've ruled out
urlencode)cursor,next_cursor,afterall behave identically)X-Cursorheader don't help)_=$RANDOMproducescf-cache-status: MISSbut the server still returns the same first page)closed=false,order=id,ascending=false— all loop){"v":1,"k":"markets","oh":"...","keys":[{"t":"string","v":"540817"}]}A multi-page walk against
/markets/keysetexhausts at 200 pages, returning ~1,000 unique markets and ~199,000 duplicates (every page is page 1).Cross-check: CLOB cursor works
For comparison,
clob.polymarket.com/marketscursor pagination works correctly:So this is gamma-keyset-specific; not a general issue with cursor handling at Polymarket.
Asks
/marketsand/events? If the cursor stays broken when legacy sunsets, every keyset client loses pagination simultaneously.clob.polymarket.com/markets(whose cursor works) a supported alternative source for the full market universe? It returns a different shape so clients would need to map.Happy to provide more diagnostic data — full request/response captures, cursor base64 decodes, etc. — if useful.