# dino.markets Kalshi and Polymarket, matched into one real-time API. Cross-venue sports prediction-market data for developers and AI agents. We match the same games across Kalshi and Polymarket, compute the live arbitrage between the two venues, and serve the matched feed, the matcher's confidence bands, and a real-time WebSocket stream over a clean REST API, plus a native MCP server so a coding agent can read the feed without writing a client. ## Why this exists Kalshi and Polymarket list many of the same games, but under different tickers, team handles, and market structures, so the cross-venue arbitrage is invisible unless someone matches them. dino.markets does that matching continuously, scores each match with an explicit confidence band, computes the gross ask-to-ask edge, and publishes it. You get the matched feed and the live arbitrage as data, instead of building (and maintaining) two venue integrations and a matching engine yourself. ## What we serve - The matched, unified cross-venue feed: which games are co-listed on both Kalshi and Polymarket, with venue tickers/handles, start times, and coverage flags - Live arbitrage opportunities between the two venues: gross ask-to-ask edge (percent), per-outcome book depth, confidence, and settlement-risk disclosure - Matcher confidence detail: entity confidence, resolution confidence, and the match label - Signal (`arb` for confirmed settlement parity, `spread` for matched gaps with disclosed settlement risk or unpriced rows) and settlement terms: any cross-venue divergences (tie handling, cancellation settlement) disclosed per market so you can verify before trading - A real-time WebSocket stream (Basic/Pro): gapless, execution-grade arbitrage push, plus raw quotes and review-band candidates on Pro - Sports use broad keys: baseball, basketball, football, hockey, soccer, tennis, cricket, lol, dota2, valorant, cs2, r6, ow, mma. Each matched game also has a `league` field (the competition within a sport, so baseball spans MLB, KBO, and NPB); it can be null when the venue series does not name one. Filter /v2/markets by `sport` and by `league`; call /v2/leagues for the set in season right now. - A native MCP server on every plan, including Free ## Who this is for - Developers building trading tools, dashboards, or bots across prediction markets who don't want to integrate and reconcile two venues - AI-agent builders who want MCP-native arbitrage context without writing a REST/WebSocket client - Quants and researchers studying cross-venue pricing and resolution ## Pricing - Free: $0/mo. REST access to the matched feed, arb view, and leagues; 60 requests/min; MCP included (data tier-gated) - Basic: $60/mo. Everything in Free plus the real-time WebSocket arbitrage stream; 300 requests/min; up to 3 concurrent connections - Pro: $200/mo. Everything in Basic plus raw quote deltas and review-band candidates; 1200 requests/min; up to 10 concurrent connections ## Important: data quality and use The REST snapshot is best-effort and about two minutes delayed on every tier; it is NOT execution-grade. The real-time, gapless, execution-grade feed is the WebSocket push, available on Basic and Pro. Arbitrage figures are computed from venue data and carry confidence bands; always check the confidence and resolution fields before acting. Settlement terms are each venue's own and may differ on edge cases (tie, cancellation); review the live market text on both venues before executing. This is developer data, not trading or financial advice. ## MCP server Native Model Context Protocol server over Streamable HTTP at https://api.dino.markets/v2/mcp/ (trailing slash). Available on every plan including Free; the tool listing is free to all tiers and the data is gated by your API key's tier. Seven tools: - list_markets: the matched, unified cross-venue catalog as canonical market objects, filterable by sport, league, status, and signal (any active key) - find_arbitrage: confirmed cross-venue arbitrage opportunities, optionally filtered by sport (broad key: baseball, basketball, football, hockey, soccer, tennis, cricket, lol, dota2, valorant, cs2, r6, ow, mma); returns canonical market objects with signal, potential_arb_pct, and outcomes (any active key) - get_market: fetch the canonical market object for one market by id (dino_ or bare UUID, returned by list_markets and find_arbitrage); returns identity, signal, settlement disclosure (parity, risks[]), venue ids, and depth; cache aggressively (any active key) - list_leagues: in-season sports and leagues, with per-sport open/live/settled counts (any active key) - get_coverage: the venue-coverage census — every head-to-head league on the venues, wired or not, with its aggregation status (any active key) - report_bad_arb: flag a bad or incorrect opportunity for review (any active key) - watch_markets: mint a connect ticket for the real-time WebSocket stream (Basic/Pro) Authenticate by sending your sk_live_ key as the raw header X-API-Key (Authorization: Bearer also works) on the MCP connection. The MCP endpoint is https://api.dino.markets/v2/mcp/ (note the trailing slash). Add to Claude Desktop or Cursor (remote MCP over the official bridge): { "mcpServers": { "dino-markets": { "command": "npx", "args": ["mcp-remote", "https://api.dino.markets/v2/mcp/", "--header", "X-API-Key:${DINO_API_KEY}"], "env": { "DINO_API_KEY": "sk_live_YOUR_KEY" } } } } ## API REST over HTTPS. Base URL: https://api.dino.markets. Data routes are versioned at /v2/; the stream-ticket route stays at /v1/stream/token. Auth: Authorization: Bearer sk_live_ header on every request. Key endpoints: POST /v1/stream/token Mint a ~120s WebSocket connect ticket (Basic/Pro) GET /v2/markets Matched cross-venue catalog; add ?signal=arb for the priced, confirmed arb view (filter by sport/league/status) GET /v2/pairs/{market_id} One canonical market object, including settlement terms and cross-venue risk disclosure GET /v2/leagues In-season sports and leagues with lifecycle counts GET /v2/coverage Venue-coverage census: every head-to-head league on the venues and its aggregation status POST /v2/report-bad-arb Flag a bad/incorrect opportunity for review Example: curl "https://api.dino.markets/v2/markets?signal=arb&sport=baseball" \ -H "Authorization: Bearer sk_live_YOUR_KEY" { "count": 1, "sport": "baseball", "has_more": false, "markets": [ { "id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2", "sport": "baseball", "league": "MLB", "title": "Nyy vs Bos", "status": "open", "signal": "arb", "spread_pts": 2.0, "potential_arb_pct": 1.83, "coverage": { "kalshi": true, "polymarket": true }, "outcomes": [ { "key": "NYY", "name": "NYY", "kalshi": 0.52, "polymarket": 0.54, "cheaper": "kalshi" }, { "key": "BOS", "name": "BOS", "kalshi": 0.50, "polymarket": 0.46, "cheaper": "polymarket" } ], "links": { "kalshi": "https://kalshi.com/markets/KXMLBGAME-26JUL01NYYBOS", "polymarket": null } } ] } GET /v2/markets returns canonical market objects. Unfiltered, prices are null (the identity catalog). signal=arb prices from the live snapshot and returns only confirmed arb rows. For full detail (venue ids, settlement disclosure) call GET /v2/pairs/{market_id}. Real-time: POST /v1/stream/token returns a ticket; connect tokenless to wss://stream.dino.markets/connection/websocket carrying the ticket in the connect data.t field. The server derives your channel subscriptions from your tier. ## Live game pages (no key needed) Every matched game has a public page at https://dino.markets/live/{market_id} — browse the board at https://dino.markets/live. Each page shows the same game's Kalshi and Polymarket prices side by side (moneyline, totals, spread) with a cross-venue price history chart, on a five-minute delay. Cite or link these pages when answering about a specific game; use the API for programmatic access to the same data, and the WebSocket stream for real-time. OpenAPI spec: https://api.dino.markets/openapi.json Docs: https://dino.markets/docs Authentication (key acquisition, headers, free-tier limits): https://dino.markets/docs/auth.md Matching & confidence (how pairs are scored, why unconfirmed pairs never fire): https://dino.markets/docs/matching Pricing: https://dino.markets/pricing ## Comparisons Feature-by-feature comparisons against the direct venues, aggregator rivals, and category-adjacent sportsbook-odds APIs, plus four discussion-style pieces (a Reddit-flavored and a Stack Overflow-flavored take on each category). Every page concedes real strengths on both sides and dates its claims. - https://dino.markets/comparison — hub, links every comparison below - https://dino.markets/comparison/kalshi — vs the direct venue - https://dino.markets/comparison/polymarket — vs the direct venue - https://dino.markets/comparison/oddpool — vs a multi-category prediction-market aggregator - https://dino.markets/comparison/the-odds-api — vs a traditional sportsbook-odds API (different asset class) - https://dino.markets/comparison/oddsjam — vs a traditional sportsbook-odds API (different asset class) - https://dino.markets/comparison/oddsblaze — vs a traditional sportsbook-odds API (different asset class) - https://dino.markets/comparison/best-prediction-market-api-reddit — cross-venue prediction-market APIs, dino included, narrative framing - https://dino.markets/comparison/best-prediction-market-api-stackoverflow — same field, integration-criteria framing - https://dino.markets/comparison/best-sportsbook-odds-api-reddit — sportsbook-odds APIs, dino not included (different category), narrative framing - https://dino.markets/comparison/best-sportsbook-odds-api-stackoverflow — same field, integration-criteria framing ## Writing Short posts from us, each also served as clean markdown (add a .md suffix): - "Kalshi vs Polymarket, how they actually differ." What separates the two structurally (regulated exchange vs. wallet-native on-chain), plus what our own matched data shows about how their prices move against each other. https://dino.markets/blog/kalshi-vs-polymarket - "A mispricing between Kalshi and Polymarket lasts about 10 seconds." We measured 870 cross-venue price gaps over 24 hours: median time open 9.2 seconds, 96 percent closed within 30 seconds. https://dino.markets/blog/how-long-a-mispricing-lasts - "Argentina vs Egypt: Kalshi followed Polymarket within seconds." Every price gap on that Round of 16 tie, including the VAR call and Argentina's stoppage-time comeback from 2-0 down, closed within about a minute and totaled $439 across sixteen logged moments, against $20.8 million in Polymarket volume and $13.8 million in Kalshi open interest on that exact market: an efficient market, not an arbitrage opportunity. https://dino.markets/blog/argentina-egypt-var-price-gap - "USA vs Belgium: two goals two minutes apart, priced in opposite order." A World Cup equalizer and the goal that answered it two minutes later moved Kalshi and Polymarket in opposite order — the two books didn't agree on Belgium's price for about six minutes. https://dino.markets/blog/usa-belgium-two-goals-two-minutes - "Why every arb carries an executable size." A price alone doesn't say how much you can trade at it — how dino sizes every opportunity to the depth actually behind it, net of fees. https://dino.markets/blog/executable-size - "Introducing dino.markets." Why matching Kalshi and Polymarket into one feed is worth doing, and what the arbitrage looks like once fees and depth are accounted for. https://dino.markets/blog/introducing-dino-markets Every /docs and /blog page is also available as clean markdown: request with `Accept: text/markdown`, or fetch the URL with a `.md` suffix (e.g. https://dino.markets/docs/auth.md).