# dino.markets MCP Related Kalshi and Polymarket prediction markets across sports, crypto, weather, and economics over the Model Context Protocol. The hosted server provides the matched catalog, confirmed arbitrage with confidence bands, and a real-time WebSocket stream. ## At a glance | | | |---|---| | Transport | Streamable HTTP | | Endpoint | `https://api.dino.markets/v2/mcp/` (trailing slash) | | Auth | `X-API-Key` header (`Authorization: Bearer` also works) — Free tier works | | Documented public tools | 8 | | Hosting | Hosted — you don't self-host | ## Install / connect Add to Claude Desktop or Cursor (remote MCP over the official bridge): ```json { "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" } } } } ``` An API key is required even on Free — there is no anonymous mode. Get one at https://dino.markets. ## Plans and limits MCP is included on every plan. REST snapshots are best-effort and about two minutes delayed on every plan. | Tier | Price | REST requests/month | REST requests/sec | WebSocket | |---|---:|---:|---:|---| | Free | $0/mo | 10,000 | 10 | Curated `sample` channel, 1 connection | | Basic | $30/mo | 1,000,000 | 10 | Full market stream, 3 connections | | Premium | $100/mo | 5,000,000 | 25 | Full market stream, 10 connections | | Pro | $200/mo | 5,000,000 | 25 | Full stream plus raw quotes and early candidates, 10 connections | ## The 8 documented public tools | Tool | Returns | Tier | |---|---|---| | `find_markets` | Free-text lookup by team, league, market title, or market family; returns ids for detail calls | Any active key | | `find_arbitrage` | Confirmed cross-venue arbitrage between Kalshi and Polymarket as native Opportunity objects — one exact selected execution leg per outcome (venue, ask, size, contract id) — optionally filtered by sport | Any active key | | `list_markets` | The matched cross-venue catalog; filter by sport, league, status, `market_type`, or `signal` (`spread`\|`candidates` — `arb` is rejected explicitly, see below) | Any active key | | `get_market` | The canonical market object for one market by id (`dino_` or bare UUID), including settlement disclosure and depth | Any active key | | `list_leagues` | In-season sports and leagues with per-sport open/live/settled counts — call this first | Any active key | | `get_spreads` | Verified cross-venue price gaps for matched pairs classified as spread-only | Any active key | | `report_bad_arb` | Flags a bad/incorrect arbitrage opportunity for operator review | Any active key | | `watch_markets` | Mints a short-lived connect ticket; Free gets `sample`, paid plans get the full stream | Any active key | `list_markets` defaults to `moneyline,outright`. `market_type` also accepts `total`, `spread`, `team_total`, `advance`, `weather_high`, `crypto_above`, `fed_decision`, `econ_print`, `cb_decision`, or `all`. The live tool description and [product manual](https://dino.markets/llms.txt) are the source of truth for settlement caveats. ## Featured example: `find_arbitrage` `find_arbitrage` returns a distinct **Opportunity** resource, not a Market object — a fired arb only ever has one selected execution leg per outcome (the venue the engine actually picked), so it can't be represented as a two-sided Market row. This is a deliberate v2 Interface change (2026-07-13): the old `GET /v2/markets?signal=arb` path is gone — `list_markets(signal="arb")` now returns a typed error directing you here. REST equivalent: ```bash curl -s "https://api.dino.markets/v2/arbitrage?sport=baseball" \ -H "Authorization: Bearer $DINO_API_KEY" ``` MCP tool call: `find_arbitrage()` (no args), or `find_arbitrage(sport="baseball")`. Response envelope (`{as_of, delay_seconds, count, opportunities:[...]}`), reproduced from the [REST API reference](https://dino.markets/docs/rest#get-v2arbitrage) rather than a fresh live capture — this repo has no server to hit directly. An empty `opportunities` array with `count: 0` is the normal resting state; a populated one looks like this: ```json { "as_of": "2026-07-13T08:00:05Z", "delay_seconds": 120, "count": 1, "opportunities": [ { "id": "opp_01J2Y9M4X8", "market_id": "dino_8f3a1c92-47e8-4c3f-b9d2-f1a8e6c4d5f2", "title": "Nyy vs Bos", "sport": "baseball", "league": "MLB", "start_time": "2026-07-13T23:05:00Z", "edge_pct": 4.2503, "roi_pct": 4.439, "fees_included": true, "fee_model": "conservative_per_contract", "cost_per_payout_usd": 0.957497, "max_contracts": 700, "max_wager_usd": 670.25, "confidence": 1.0, "detected_at": "2026-07-13T08:00:00Z", "priced_at": "2026-07-13T08:00:05Z", "legs": [ { "outcome": "NYY", "venue": "kalshi", "side": "yes", "ask_usd": 0.44, "stake_fraction": 0.480419, "available_contracts": 1000, "contract_id": "KXMLBGAME-NYY-BOS-NYY", "url": null }, { "outcome": "BOS", "venue": "polymarket", "side": null, "ask_usd": 0.49, "stake_fraction": 0.519581, "available_contracts": 700, "contract_id": "tok_bos", "url": null } ] } ] } ``` `edge_pct` and `roi_pct` are conservative modeled returns after fees; `fees_included` is explicit; `fee_model: "conservative_per_contract"` means the producer models one contract per selected leg and the API scales that cost to `max_contracts`, so quantity-level venue rounding can differ. `ask_usd` is USD paid per contract (payout $1 if that outcome wins); `max_contracts` is the snapshot executable equal contract count per outcome, and `max_wager_usd` is the conservative fee-inclusive capital estimate at that size. `as_of` is the newest `priced_at` in the response — use it, not request time, to judge freshness. Call `list_markets` (unfiltered, or with `signal=spread`) to see the matched catalog even when no arb is currently confirmed. ## The Market signal vs. the Opportunity resource `list_markets` and `get_market` return **canonical Market objects**, each carrying its own `signal` field re-derived on every call — never a cached label: - `signal:"arb"` is earned by `decide_signal()` on the two-sided catalog join: status open with 2+ confirmations, confirmed settlement parity, no high/critical risk, and every outcome priced on both venues. - `signal:"spread"` is everything else: an observed price gap (`spread_pts`) that hasn't cleared the arb gates — an **observation**, not a return you can act on. These two signals and the `find_arbitrage` Opportunity are related but not identical: a fired Opportunity's ledger record stores exactly one selected venue per outcome (the side the engine actually chose to execute), while a Market's `signal` field requires both venues priced on every outcome to call it `"arb"`. Because of that structural difference, `get_market`/`list_markets` can show `signal:"spread"` for a pair `find_arbitrage` is concurrently reporting as a real, live Opportunity — `find_arbitrage` is the authoritative collection of currently open, executable arbs. Call it directly rather than filtering `list_markets` for `signal:"arb"` (which is rejected explicitly, see above). ## Settlement disclosure and confidence bands Most aggregators give you a number. dino gives you the disclosure to verify it before trading. `get_market` (and `GET /v2/pairs/{id}`) returns: - `settlement.parity` (bool) — whether the two venues' settlement terms have been confirmed to agree - `settlement.risks[]` — any disclosed cross-venue divergence (tie handling, cancellation settlement, etc.) - `settlement.verdict` — `"agree"` | `"diverge"` | `"edge_case"` | `null` (null until the matcher has reconciled the two venues' texts — typically after the market resolves) - `confidence.entity` / `confidence.resolution` — the matcher's own confidence that the two venues are quoting the same real-world event and will resolve the same way Read `settlement.risks` and `confidence` before treating any `arb` row as tradeable — the API surfaces the same venue text (`settlement.kalshi_text` / `settlement.poly_text`) it used to compute the verdict, so you can check it yourself rather than trust a single number. ## Localized documentation Product documentation is available in these languages. This repository remains the English MCP reference. | Language | Docs | MCP server | |---|---|---| | 日本語 | [Docs](https://dino.markets/ja/docs) | [MCP](https://dino.markets/ja/docs/mcp) | | 한국어 | [Docs](https://dino.markets/ko/docs) | [MCP](https://dino.markets/ko/docs/mcp) | | 简体中文 | [Docs](https://dino.markets/zh/docs) | [MCP](https://dino.markets/zh/docs/mcp) | | Español | [Docs](https://dino.markets/es/docs) | [MCP](https://dino.markets/es/docs/mcp) | ## Links - llms.txt: https://dino.markets/llms.txt - OpenAPI: https://api.dino.markets/openapi.json - Docs: https://dino.markets/docs - Pricing: https://dino.markets/pricing - Sibling repo: [dino-markets-skills](https://github.com/dino-markets/dino-markets-skills) — Agent Skills built on this MCP server ## Disclaimer This is developer data, not trading or financial advice.