Compare Kalshi and Polymarket safely
Comparing Kalshi and Polymarket starts with the contract. Two listings can describe the same event in plain English while paying on different outcomes, observation windows, data sources, or boundaries. Putting their prices beside each other before checking those terms creates a precise-looking comparison of two different claims.
Dino matches related listings into one Market object and keeps settlement differences attached to that object. This tutorial uses the Python SDK to inspect one pair and calculate a price gap only after the contract checks are reviewed.
Pull a matched Market and its detail
Create a free API key and install the client. Then request an open market family. This example uses weather because its station and bucket differences are easy to see in the disclosure:
export DINO_API_KEY=sk_live_...
pip install dino-markets
from dino_markets import Dino
client = Dino()
catalog = client.markets(
category="weather",
market_type="weather_high",
status="open,live",
limit=20,
)
summary = next(
(
market
for market in catalog["markets"]
if market["coverage"]["kalshi"]
and market["coverage"]["polymarket"]
),
None,
)
if summary is None:
raise SystemExit("No two-venue weather comparison is available right now.")
market = client.market(summary["id"])
The list result supplies a stable Dino id plus the current delayed prices. client.market(id) adds normalized context, venue identities, depth, and settlement disclosure for a full comparison.
Turn the disclosure into a review checklist
The following function makes four checks visible before any price calculation:
def comparison_checks(market):
settlement = market.get("settlement") or {}
risks = settlement.get("risks") or []
served_blocking = [
risk
for risk in risks
if risk.get("severity") == "high"
]
fully_priced = bool(market["outcomes"]) and all(
outcome.get("kalshi") is not None
and outcome.get("polymarket") is not None
for outcome in market["outcomes"]
)
return {
"entity_match_served": market["match"] == "high_confidence",
"full_price_coverage": fully_priced,
"settlement_parity": settlement.get("parity") is True,
"served_blocking_risks": served_blocking,
}
checks = comparison_checks(market)
print(checks)
match answers the identity question: Dino has enough evidence that the listings represent the same event or underlying claim to serve them together. Full price coverage answers whether every outcome can be compared across both venues. Settlement parity and the served risk list show whether the matched outcomes pay under compatible terms, subject to the cancellation disclosure below.
A Market can be a valid cross-venue comparison while carrying a settlement caveat. In that case its signal remains spread, and the caveat belongs beside the prices. A confirmed Opportunity has a higher gate and comes from the separate /v2/arbitrage resource.
Read the contract in layers
Use the Market detail fields as a fixed review order:
| Check | Field | What to verify |
|---|---|---|
| Identity | match, title, category context | Both venue listings refer to the same event or claim. |
| Outcomes | outcomes[].key, outcomes[].name | The buckets cover the same real-world results. |
| Coverage | coverage, outcome prices | Both venues have a real price wherever the comparison needs one. |
| Settlement | settlement.parity, settlement.risks[], settlement.kalshi_text, settlement.poly_text | Settlement sources and cutoff boundaries are compatible or disclosed. Read each venue's rules text for cancellation wording. |
The served settlement.risks[] deliberately omits internal cancel_* events. Read kalshi_text and poly_text for each venue's cancellation terms. Dino computes settlement.parity and signal from the complete internal risk set before filtering those events from the public risk list.
Weather markets show why each layer matters. Two daily-high contracts may name the same city while settling from different weather stations. Their temperature brackets may also be offset by one degree. Dino keeps each venue's actual buckets and reports overlaps instead of inventing a combined price.
Crypto threshold markets expose a different boundary issue. One venue can settle “at or above” a strike while the other uses “above.” A price gap at the exact threshold then refers to contracts with different winning sets, even when both titles mention the same asset and number.
Calculate price differences last
Once the disclosure has been reviewed, calculate the observed difference per fully priced outcome:
for outcome in market["outcomes"]:
kalshi = outcome.get("kalshi")
polymarket = outcome.get("polymarket")
if kalshi is None or polymarket is None:
continue
gap_pts = abs(kalshi - polymarket) * 100
print(
outcome["name"],
f"Kalshi {kalshi:.2f}",
f"Polymarket {polymarket:.2f}",
f"gap {gap_pts:.1f} pts",
)
The result is a cross-venue spread at the Market's priced_at timestamp. It says how far apart the two observed prices are. Execution requires current order-book data, available size, fees, and compatible settlement terms. This is developer data, not trading or financial advice.
Use How Dino matches markets for the identity and firing gates, and the Market object reference for every field used above. The live boards show the same disclosure in a human-readable view.
More from the blog
Monitor Kalshi and Polymarket with Python
Build a Python monitor for matched Kalshi and Polymarket markets, then extend the delayed REST snapshot with real-time updates.
Kalshi + Polymarket MCP for Claude and Cursor
Connect Claude or Cursor to Dino's Kalshi and Polymarket MCP server and inspect matched markets with settlement context.
Institutional money reached prediction markets. Commodities are the test
ICE and Tradeweb have backed Polymarket and Kalshi. Commodity liquidity will show whether institutional infrastructure becomes trading flow.