Skip to main content

What you’ll get

Every recorded version of one market’s contract terms with what changed between them, the rulebooks in force above it with their settlement sources, and a live feed of rule changes across every market. Then a checklist for the case that matters most: the terms a position or a proof rests on changed underneath it. On 2026-09-17 a Polymarket election market’s resolution metadata changed at 12:01 UTC, and a Kalshi tennis market’s strike and close time changed at 12:03.

Prerequisites

  • An API key in ROUTEUR_API_KEY
  • Python 3 with requests
1

Read a market's rules and their history

GET /v1/markets/{venue}/{id}/rules works for any market ever recorded, listed or not.
Trimmed, observed 2026-09-17 12:47 UTC (the rule text itself is in rules, left out here):
The market’s own text did not change; its rulebook did. umaResolutionStatuses went from [] to ["proposed", "proposed"]: a resolution has been proposed on UMA, and the market is about to settle.
2

Print the versions, diffs and settlement sources

This script lists every contract version with its diff, the rulebooks in force with where each says the market settles, the ledger, and then reads each rulebook’s own history to say whether a settlement source changed.
Python
Output for the Polymarket market above, observed 2026-09-17 12:48 UTC:
And for a Kalshi market whose own contract changed, the same script:
3

Follow rule changes as they happen

The rules topic of /v1/stream sends one rule_change event per change, for every market or only the ones you name in markets.
An event observed 2026-09-17 12:03 UTC (replayed with a cursor from earlier in the hour):
Set TOPICS = "rules" in the Stream consumer and replace its handler with one that keys on what changed:
Python
Between 12:01 and 12:36 UTC on 2026-09-17, 699 rule_change events arrived, 695 of them on Polymarket. 574 had changed_fields: [] (a term the recorded fields do not show, mostly resolution timestamps moving as games ended), 46 changed only game_starts_at, and 79 changed strike or closes_at. The last group is the one a position cares about; the handler above marks it TERMS or TIMING.
4

When the settlement source changes

A settlement source is where the venue says it will read the result. It lives in the rulebooks above the contract: a Kalshi event’s settlement_sources, a Polymarket market’s resolutionSource and umaResolutionStatuses, and the venue’s contract terms document. A change there does not change the market’s rules_hash, so it is easy to miss. When the stream sends a rule_change with scope other than contract, or the rulebook’s changed_fields names a source, do this:
  1. Read the diff. GET /v1/rulebooks/{venue}/{kind}/{id} has every version with changed_fields, diff, and for Kalshi the terms document with its own diff. Decide whether the new source can disagree with the old one.
  2. Re-check every proof the market is in. A proof holds for a contract version, and a relation’s evidence names what both contracts settle on. GET /v1/relations/{a}/{b} returns reproduced: false, or 404 relation_not_found, when the graph can no longer relate the current versions; a relation that survives is safe only if its evidence still describes the settlement you read in step 1.
  3. Treat leads on the pair as void until the next graph run confirms them. Runs finish hourly (GET /v1/status), and a lead’s depth.limited_by becomes informational when the venues could settle differently.
  4. If you hold a position, compare the sources, not the hashes. The script’s last section prints SETTLEMENT SOURCE CHANGED with the names before and after. A change from a named agency to none, or to a different one, is the case to act on.
A Polymarket umaResolutionStatuses going from [] to ["proposed", ...], as above, is not a source change: it means resolution has started on the same source. Expect the market to close and the price to go to 0 or 100¢.

Read the result

Pitfalls

  • changed_fields: [] with a new hash is normal. It means the venue changed something the recorded fields do not show, such as an expiration or settlement timestamp. The summary says so. Do not treat every new version as a rules rewrite; do read the ledger when one lands on a market you hold.
  • Kalshi series rulebooks were not recorded on 2026-09-17. GET /v1/rulebooks/kalshi/series/KXBTCD (and KXMLBTOTAL, KXSB, KXITFMATCH) returned 404 rulebook_not_found; only event rulebooks were present. The script skips a missing rulebook; the series’ contract terms document is then not available through this endpoint.
  • A close time moving earlier is a settlement. The Kalshi tennis market’s closes_at moved from 2026-10-01 to 2026-09-17 12:00:40 because the match ended. Treat it as the market resolving, not as a rule change to argue with.
  • Hashes are 64 hex characters. The script prints 12 for readability; compare full values in code.
  • The stream replays one hour. A watcher that was down longer must reread /rules for the markets it cares about after the reset event.
  • Proof trail: the proof names the contract versions it holds for.
  • Stream consumer: a resumable consumer for the rules topic.
  • Venues: how each venue publishes its terms.
  • GET /v1/rulebooks/{venue}/{kind}/{id}: every version of a rulebook and the markets under it.