{"openapi":"3.1.0","info":{"title":"ParlayAPI","description":"Real-time sports odds aggregation from **15 sources** updated every 60-90 seconds.\n\n**79 sports** including MLB, NFL, NBA, NHL, MMA/UFC, esports, volleyball, and 51 soccer leagues.\n\n**Sportsbooks:** DraftKings, FanDuel, Caesars, Bovada, Pinnacle, Fliff (real American odds on props).\n\n**DFS/Props:** PrizePicks, Underdog Fantasy, Betr, Pick6, Sleeper.\n\n**Exchanges:** Novig (peer-to-peer, bid/ask + volume), ProphetX (exchange odds).\n\n**Features:**\n- Player props with American odds from 10+ sources\n- Exchange market liquidity (volume, bid/ask) from Novig\n- Arbitrage detection across all books\n- +EV finder (sharp vs soft book comparison)\n- Cross-book odds comparison\n- Pinnacle closing lines for CLV analysis\n- 1.5M+ historical odds records (1999-present)\n- Live/in-play odds\n- SSE streaming (Pro tier+)\n\n**Authentication:** Pass `apiKey` as query parameter, `X-API-Key` header, or `Authorization: Bearer <key>`.\n\n**Free tier:** 1,000 credits/month. Sign up at [parlay-api.com](https://parlay-api.com).","version":"3.2.0"},"paths":{"/v1/health":{"get":{"summary":"Health Check","description":"Health check: verifies DB connectivity and data freshness.\n\nAliased at /healthz for the failover Cloudflare Worker (k8s\nconvention) and at /v1/health for symmetry with the rest of the\nversioned API surface (#043).","operationId":"health_check_v1_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/healthz":{"get":{"summary":"Health Check","description":"Health check: verifies DB connectivity and data freshness.\n\nAliased at /healthz for the failover Cloudflare Worker (k8s\nconvention) and at /v1/health for symmetry with the rest of the\nversioned API surface (#043).","operationId":"health_check_healthz_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/health":{"get":{"summary":"Health Check","description":"Health check: verifies DB connectivity and data freshness.\n\nAliased at /healthz for the failover Cloudflare Worker (k8s\nconvention) and at /v1/health for symmetry with the rest of the\nversioned API surface (#043).","operationId":"health_check_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/billing/checkout":{"post":{"tags":["billing"],"summary":"Create Checkout","description":"Create a Stripe Checkout Session. Returns checkout URL.","operationId":"create_checkout_billing_checkout_post","parameters":[{"name":"tier","in":"query","required":true,"schema":{"type":"string","title":"Tier"}},{"name":"promo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Promo"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/billing/portal":{"post":{"tags":["billing"],"summary":"Create Portal","description":"Create Stripe Customer Portal session for self-service management.","operationId":"create_portal_billing_portal_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/billing/success":{"get":{"tags":["billing"],"summary":"Checkout Success","operationId":"checkout_success_billing_success_get","parameters":[{"name":"session_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/billing/success-page":{"get":{"tags":["billing"],"summary":"Checkout Success Page Alias","operationId":"checkout_success_page_alias_billing_success_page_get","parameters":[{"name":"session_id","in":"query","required":false,"schema":{"type":"string","default":"","title":"Session Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/billing/cancel":{"get":{"tags":["billing"],"summary":"Checkout Cancel","operationId":"checkout_cancel_billing_cancel_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/billing/cancel-page":{"get":{"tags":["billing"],"summary":"Checkout Cancel Page Alias","operationId":"checkout_cancel_page_alias_billing_cancel_page_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/signup":{"get":{"tags":["auth"],"summary":"Signup Page","operationId":"signup_page_signup_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"tags":["auth"],"summary":"Signup Submit","operationId":"signup_submit_signup_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_signup_submit_signup_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/login":{"get":{"tags":["auth"],"summary":"Login Page","operationId":"login_page_login_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"tags":["auth"],"summary":"Login Submit","operationId":"login_submit_login_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_login_submit_login_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/logout":{"get":{"tags":["auth"],"summary":"Logout","operationId":"logout_logout_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/auth/magic":{"get":{"tags":["auth"],"summary":"Magic Link Login","description":"Land here from a magic-link URL. Sets the session cookie from the\nsid query param and redirects to the dashboard.\n\nUsed by the agent-signup flow: a session is created server-side at\nsignup time and embedded in the URL; visiting the URL claims it.","operationId":"magic_link_login_auth_magic_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/forgot-password":{"get":{"tags":["auth"],"summary":"Forgot Password Page","operationId":"forgot_password_page_forgot_password_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"tags":["auth"],"summary":"Forgot Password Submit","description":"Accept any email, generate a reset token, email the link.\nAlways returns success to avoid enumerating which emails exist.","operationId":"forgot_password_submit_forgot_password_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_forgot_password_submit_forgot_password_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/reset-password":{"get":{"tags":["auth"],"summary":"Reset Password Page","operationId":"reset_password_page_reset_password_get","parameters":[{"name":"token","in":"query","required":false,"schema":{"type":"string","default":"","title":"Token"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"post":{"tags":["auth"],"summary":"Reset Password Submit","operationId":"reset_password_submit_reset_password_post","requestBody":{"required":true,"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_reset_password_submit_reset_password_post"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/agent/signup":{"post":{"tags":["agent"],"summary":"Agent Signup","description":"Create a free-tier account for an email and return an API key\nplus a magic-link claim URL the user clicks to verify and access\nthe dashboard.\n\nIdempotent on email: if an account already exists, the API key is\nNOT returned (security), only a login URL the user can use to recover.","operationId":"agent_signup_v1_agent_signup_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/agent/magic-link":{"post":{"tags":["agent"],"summary":"Agent Magic Link","description":"Send a magic login link to an email. Always returns 200 so callers\ncannot enumerate which emails have accounts.","operationId":"agent_magic_link_v1_agent_magic_link_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/agent/checkout-link":{"post":{"tags":["agent"],"summary":"Agent Checkout Link","description":"Generate a Stripe Checkout URL for a tier upgrade. The agent pastes\nthis to the user; the user clicks, pays, and the account is upgraded\nvia Stripe webhook (existing infrastructure).","operationId":"agent_checkout_link_v1_agent_checkout_link_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/dashboard":{"get":{"tags":["dashboard"],"summary":"Dashboard Page","operationId":"dashboard_page_dashboard_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/dashboard/billing":{"get":{"tags":["dashboard"],"summary":"Billing Page","description":"Self-serve tier upgrade / downgrade page.\n\nThe legacy /dashboard/billing link from the dashboard upgrade\nbanner used to 404 (the page was referenced but not implemented).\nCustomers who'd hit their credit cap saw the 'Upgrade tier' button,\nclicked it, and landed on a dead page — then went hunting through\nthe Stripe portal where the only visible action was 'Cancel'. One\nconfirmed support ticket (Bryce, 2026-05-11) from this exact path.\n\nThis page shows the customer's current tier, recent usage, and\none-click upgrade buttons that POST directly to /billing/checkout\nfor each higher tier. Downgrade routes through the Stripe Customer\nPortal which they can also reach from here.","operationId":"billing_page_dashboard_billing_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/support":{"get":{"tags":["public_support"],"summary":"Support Page","description":"Public contact form. No auth.","operationId":"support_page_support_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"post":{"tags":["public_support"],"summary":"Support Submit","operationId":"support_submit_support_post","requestBody":{"content":{"application/x-www-form-urlencoded":{"schema":{"$ref":"#/components/schemas/Body_support_submit_support_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks":{"get":{"tags":["webhooks"],"summary":"List Webhooks","operationId":"list_webhooks_v1_webhooks_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"tags":["webhooks"],"summary":"Create Webhook","operationId":"create_webhook_v1_webhooks_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookCreate"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}":{"get":{"tags":["webhooks"],"summary":"Get Webhook","description":"Look up a single webhook config by id. iter_054 #446: was 405.\nCustomer who lost the create-time secret can at least re-fetch the\nrest of their config. (Secret itself is NEVER returned again; ours\nis a one-time-show secret per Stripe convention.)","operationId":"get_webhook_v1_webhooks__webhook_id__get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"tags":["webhooks"],"summary":"Delete Webhook","operationId":"delete_webhook_v1_webhooks__webhook_id__delete","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/deliveries":{"get":{"tags":["webhooks"],"summary":"List Webhook Deliveries","description":"List recent delivery attempts for a webhook. iter_054 #446:\nreliability monitoring is the #2 most-requested webhook feature.\nStores up to ~30 days of delivery history per webhook in the\nwebhook_deliveries table; older rows are pruned by the retention\ncleanup.","operationId":"list_webhook_deliveries_v1_webhooks__webhook_id__deliveries_get","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":50,"title":"Limit"}},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","minimum":0,"default":0,"title":"Offset"}},{"name":"status","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter: 'success', 'failed', or omit for all","title":"Status"},"description":"Filter: 'success', 'failed', or omit for all"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/webhooks/{webhook_id}/test":{"post":{"tags":["webhooks"],"summary":"Test Webhook","description":"Fire a `test` event so the customer can verify their endpoint.","operationId":"test_webhook_v1_webhooks__webhook_id__test_post","parameters":[{"name":"webhook_id","in":"path","required":true,"schema":{"type":"integer","title":"Webhook Id"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live":{"get":{"tags":["live"],"summary":"Live Page","description":"Serve the live odds dashboard.","operationId":"live_page_live_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/live/api/data_flow":{"get":{"tags":["live"],"summary":"Live Data Flow","description":"Per-source freshness for the /live page status strip.\n\nFor each active sportsbook reports the last time we POLLED the\nupstream, not the last time we wrote a price-change row. The two\ndiffer: change-detection suppresses no-change writes, so\nMAX(timestamp_ms) on prop_snapshots reports the last price MOVE,\nwhich for many books is much older than the last poll cycle. Using\npoll_pulses (Redis-backed heartbeats, written every poll cycle\nregardless of change-detection) gives the true \"last poll\" age.\n\nFalls back to MAX(timestamp_ms) for sources that don't write pulses\nyet, so we keep the chip visible (with an honest 'fallback: true'\ntag so we can audit which sources still need pulse coverage).\n\nCached 5s, public, no auth.","operationId":"live_data_flow_live_api_data_flow_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/live/api/sports":{"get":{"tags":["live"],"summary":"Live Sports","description":"Active sports with event counts. No auth required. Cached 60s.","operationId":"live_sports_live_api_sports_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/live/api/games":{"get":{"tags":["live"],"summary":"Live Games","description":"Games for a sport with odds preview. Anonymous users get limited books.","operationId":"live_games_live_api_games_get","parameters":[{"name":"sport","in":"query","required":true,"schema":{"type":"string","title":"Sport"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live/api/game/{event_id}":{"get":{"tags":["live"],"summary":"Live Game Detail","description":"Full game detail with all books + props. Costs 1 credit.","operationId":"live_game_detail_live_api_game__event_id__get","parameters":[{"name":"event_id","in":"path","required":true,"schema":{"type":"string","title":"Event Id"}},{"name":"sport","in":"query","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"home","in":"query","required":false,"schema":{"type":"string","title":"Home"}},{"name":"away","in":"query","required":false,"schema":{"type":"string","title":"Away"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live/api/search":{"get":{"tags":["live"],"summary":"Live Search","description":"Search teams/players/tournaments across all sports. No auth required.\n\nSport-key matching uses the user's literal query plus a space-to-\nunderscore-normalized variant, so 'ITF Kurume' matches\n'tennis_itf_women_kurume' (the sport_key the FD in-play source\nemits for that tournament).\n\niter_049 #423: capped at `?limit=` (default 25) so broad-substring\nqueries like 'mlb' don't blow up trigram-index work. Was previously\nunbounded; high-cardinality queries timed out at 20s.","operationId":"live_search_live_api_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"title":"Q"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"description":"Max results to return. iter_049 #423: previously unbounded; broad queries like 'mlb' could time out. Default 25.","default":25,"title":"Limit"},"description":"Max results to return. iter_049 #423: previously unbounded; broad queries like 'mlb' could time out. Default 25."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live/api/pbp":{"get":{"tags":["live"],"summary":"Live Pbp","description":"Recent play-by-play events for a sport (or specific game). Free, no auth.\nPowers the 'What's happening' marquee on the /live page.","operationId":"live_pbp_live_api_pbp_get","parameters":[{"name":"sport","in":"query","required":true,"schema":{"type":"string","description":"Sport key, e.g. basketball_nba","title":"Sport"},"description":"Sport key, e.g. basketball_nba"},{"name":"event_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Match id; omit for sport-wide","title":"Event Id"},"description":"Match id; omit for sport-wide"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20,"minimum":1,"default":5,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live/api/disagreement":{"get":{"tags":["live"],"summary":"Live Disagreement","description":"Top cross-book line disagreements right now, across all sports.\nFree + no auth (showcase teaser). Updates every ~15s.\n\nPowers the 'Books disagree right now' sidebar on /live.\nFull per-event detail with all books still requires the Pro+\n/v1/sports/{sport_key}/live/disagreement endpoint.\n\nUses singleflight via cached_call so concurrent misses do not fan\nout to N DB queries (the thundering-herd that pinned Postgres\nearlier tonight).","operationId":"live_disagreement_live_api_disagreement_get","parameters":[{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":25,"minimum":1,"description":"Top-N disagreements across all sports","default":8,"title":"Limit"},"description":"Top-N disagreements across all sports"},{"name":"min_deviation_pct","in":"query","required":false,"schema":{"type":"number","maximum":1,"minimum":0,"description":"Min cross-book deviation to surface","default":0.025,"title":"Min Deviation Pct"},"description":"Min cross-book deviation to surface"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/live/api/sparkline":{"get":{"tags":["live"],"summary":"Live Sparkline","description":"Recent line history for a single game, suitable for a tiny\ninline sparkline on the live game card. Returns one point per minute\nof moneyline (or h2h) movement across all books, averaged.\nFree + no auth. Updates every 30s.","operationId":"live_sparkline_live_api_sparkline_get","parameters":[{"name":"sport","in":"query","required":true,"schema":{"type":"string","title":"Sport"}},{"name":"home_team","in":"query","required":true,"schema":{"type":"string","title":"Home Team"}},{"name":"away_team","in":"query","required":true,"schema":{"type":"string","title":"Away Team"}},{"name":"minutes","in":"query","required":false,"schema":{"type":"integer","maximum":180,"minimum":5,"default":30,"title":"Minutes"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/sse":{"get":{"summary":"Sports Live Sse","description":"Server-Sent Events stream of live state changes.\n\nTier-gated: paid tier (starter+). Billed per delivered event.","operationId":"sports_live_sse_v1_sports__sport_key__live_sse_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"match_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Match ID to subscribe to. Omit or use '*' for all live matches in this sport.","title":"Match Id"},"description":"Match ID to subscribe to. Omit or use '*' for all live matches in this sport."},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/points":{"get":{"summary":"Sports Live Points","description":"One-shot snapshot of live state. Free tier OK. 1 credit per call.","operationId":"sports_live_points_v1_sports__sport_key__live_points_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"match_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Match ID. Omit for all currently in-play matches in this sport.","title":"Match Id"},"description":"Match ID. Omit for all currently in-play matches in this sport."},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/book_latency":{"get":{"summary":"Sports Live Book Latency","description":"Per-book latency for live games in this sport.\n\nFor each book (DK / FD / Caesars / BetMGM / Pinnacle / etc.) we\ncompute lag_seconds = primary_PBP_age - book_odds_last_update.\nPositive lag means the book is behind reality (their lines might be\nstale; exploit before rebalance).\n\nUse case: arb scanner customers query this every few seconds and\nsurface \"FD has 8s lag on Yankees-Rangers right now\" alerts.\n\nPro+ tier. 5 credits per call (rich derived data).","operationId":"sports_live_book_latency_v1_sports__sport_key__live_book_latency_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/source-health":{"get":{"summary":"Sports Live Source Health","description":"Customer-facing source-freshness diagnostic.\n\nReturns per-source freshness for the requested sport: which feeds\nare emitting events, when they last did, and how many events landed\nin the last 5 minutes. Use this to detect when a source goes stale\nso your bot doesn't trade on dead data.\n\nResponse shape (one row per source actively emitting for this sport):\n    [\n        {\n            \"sport_key\": \"basketball_nba\",\n            \"source\": \"nba_live\",\n            \"role\": \"primary\",\n            \"events_last_5min\": 142,\n            \"seconds_since_last_event\": 3.2,\n            \"latest_capture_ms\": 1778214028111\n        },\n        ...\n    ]\n\nFree tier OK. 1 credit per call. Recommended polling cadence: every\n30 seconds. If a source you care about shows seconds_since_last_event\n> 60 during a known-live game, that source has gone stale; failover\nto another source or the league-API path.\n\n**Empty-when-healthy semantics** (iter_064 #479): the pbp_source_health\ntable is populated by the source-health monitor only when a feed\ntransitions into a degraded state (slow / silent / errored). A\nresponse of `{count: 0, results: []}` means every tracked source\nfor the sport is firing within its expected cadence. Treat empty\nas a positive signal, not a missing-data signal. To verify the\nunderlying source is actually live, cross-reference\n`/live/api/data_flow` which always returns the per-source pulse\nsnapshot.","operationId":"sports_live_source_health_v1_sports__sport_key__live_source_health_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/period_markets":{"get":{"summary":"Sports Live Period Markets","description":"Return latest period market lines for a sport.\n\nOpen to all tiers. 2 credits per call.","operationId":"sports_live_period_markets_v1_sports__sport_key__live_period_markets_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"FT / 1H / 2H / Q1 / Q2 / Q3 / Q4 / OT or 'all'","default":"all","title":"Period"},"description":"FT / 1H / 2H / Q1 / Q2 / Q3 / Q4 / OT or 'all'"},{"name":"match_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional source-native match id","title":"Match Id"},"description":"Optional source-native match id"},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional book filter","title":"Source"},"description":"Optional book filter"},{"name":"market","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional market filter (spread/total/h2h)","title":"Market"},"description":"Optional market filter (spread/total/h2h)"},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/disagreement":{"get":{"summary":"Sports Live Disagreement","description":"Cross-book disagreement diagnostic for in-play markets.\n\nReturns one row per (match, period, market, side) with each book's\nlatest line + price plus deviation from a chosen anchor (median or\nPinnacle). Use this to find +EV opportunities during fast scoring\nruns where soft books haven't caught up to sharp lines yet.\n\nTier gate: Pro+. 5 credits per call.\n\nExample response (basketball_nba Q3 total):\n    {\n        \"sport_key\": \"basketball_nba\",\n        \"period\": \"Q3\",\n        \"market\": \"total\",\n        \"anchor\": \"median\",\n        \"as_of_ms\": 1778342828915,\n        \"results\": [\n            {\n                \"match_id\": \"0042500301\",\n                \"home_team\": \"Lakers\", \"away_team\": \"Thunder\",\n                \"side\": \"over\",\n                \"anchor_line\": 51.5,\n                \"books\": [\n                    {\"source\": \"pinnacle\", \"line\": 51.5, \"price\": -110,\n                     \"deviation_abs\": 0.0, \"deviation_pct\": 0.0,\n                     \"age_seconds\": 2.1},\n                    {\"source\": \"dk_web\", \"line\": 50.5, \"price\": -115,\n                     \"deviation_abs\": 1.0, \"deviation_pct\": 0.0194,\n                     \"age_seconds\": 1.4}\n                ]\n            }\n        ]\n    }\n\nFilter on `deviation_pct > 0.02` client-side to surface the actually\nactionable disagreements (anything within median noise gets dropped).","operationId":"sports_live_disagreement_v1_sports__sport_key__live_disagreement_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"FT / 1H / 2H / Q1-Q4 / OT / P1-P3 / F5 / F7 or 'all'","default":"all","title":"Period"},"description":"FT / 1H / 2H / Q1-Q4 / OT / P1-P3 / F5 / F7 or 'all'"},{"name":"market","in":"query","required":false,"schema":{"type":"string","description":"spread / total / h2h","default":"total","title":"Market"},"description":"spread / total / h2h"},{"name":"side","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional side filter (over/under for total, home/away for spread/h2h). Omit to get all sides per book.","title":"Side"},"description":"Optional side filter (over/under for total, home/away for spread/h2h). Omit to get all sides per book."},{"name":"anchor","in":"query","required":false,"schema":{"type":"string","description":"Reference for deviation: 'median' (across all books) or 'pinnacle'","default":"median","title":"Anchor"},"description":"Reference for deviation: 'median' (across all books) or 'pinnacle'"},{"name":"max_age_s","in":"query","required":false,"schema":{"type":"integer","maximum":300,"minimum":1,"description":"Drop books whose latest poll is older than this many seconds. Default 90s catches Bovada's slower polling cycle alongside Pinnacle's tight one. Lower it for stricter freshness, raise it for sparse periods (deep Q4, OT).","default":90,"title":"Max Age S"},"description":"Drop books whose latest poll is older than this many seconds. Default 90s catches Bovada's slower polling cycle alongside Pinnacle's tight one. Lower it for stricter freshness, raise it for sparse periods (deep Q4, OT)."},{"name":"min_books","in":"query","required":false,"schema":{"type":"integer","maximum":10,"minimum":1,"description":"Minimum number of books required to emit a row. Default 2 because rows with n_books=1 can't be disagreeing with anyone (max_deviation_pct=0 always). Pass 1 to also see markets where only one book has a recent quote (useful for diagnosing which books are slow to refresh).","default":2,"title":"Min Books"},"description":"Minimum number of books required to emit a row. Default 2 because rows with n_books=1 can't be disagreeing with anyone (max_deviation_pct=0 always). Pass 1 to also see markets where only one book has a recent quote (useful for diagnosing which books are slow to refresh)."},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live/period_markets/sources":{"get":{"summary":"Sports Live Period Sources","description":"List which books currently have period markets for this sport.","operationId":"sports_live_period_sources_v1_sports__sport_key__live_period_markets_sources_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","title":"Apikey"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/sports/{sport_key}/period_markets":{"get":{"summary":"Historical Period Markets","description":"Durable per-distinct-state archive of period market line movement.\n\nEach row is one (match_id, source, period_key, market, side, line,\nprice) state with `first_seen_ms` (when the book first wrote that\nstate) and `last_seen_ms` (the latest poll that re-confirmed it).\n\nReplaying movement: order by first_seen_ms within a (match, period,\nmarket, side) group; consecutive distinct line / price values are\nthe moves. The same archive feeds both pre-game and in-play movement,\nso a Q3 line that opens at 55.5, drops to 54.5 mid-game, and closes\nat 53.5 produces three rows.\n\nTier gating: 5 credits per call (mirrors /historical/closing-odds).","operationId":"historical_period_markets_v1_historical_sports__sport_key__period_markets_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","description":"FT / 1H / 2H / Q1-Q4 / OT / P1-P3 or 'all'","default":"all","title":"Period"},"description":"FT / 1H / 2H / Q1-Q4 / OT / P1-P3 or 'all'"},{"name":"match_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional source-native match id","title":"Match Id"},"description":"Optional source-native match id"},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional book filter","title":"Source"},"description":"Optional book filter"},{"name":"market","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional market filter (spread/total/h2h)","title":"Market"},"description":"Optional market filter (spread/total/h2h)"},{"name":"home_team","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by home team substring","title":"Home Team"},"description":"Filter by home team substring"},{"name":"away_team","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by away team substring","title":"Away Team"},"description":"Filter by away team substring"},{"name":"dateFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD inclusive","title":"Datefrom"},"description":"YYYY-MM-DD inclusive"},{"name":"dateTo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"YYYY-MM-DD inclusive","title":"Dateto"},"description":"YYYY-MM-DD inclusive"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":20000,"minimum":1,"default":5000,"title":"Limit"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","description":"Required API key.","title":"Apikey"},"description":"Required API key."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/affiliates":{"get":{"summary":"List Affiliates","description":"List all active affiliate links for a country.\n\nNo API key required (public endpoint). Returns book_key, name, bonus\ntext, and a relative redirect URL the client should use (so we get\nthe click attribution).","operationId":"list_affiliates_v1_affiliates_get","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"US","title":"Country"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/go/{book_key}":{"get":{"summary":"Affiliate Redirect","description":"Redirect to the affiliate signup URL, log the click.","operationId":"affiliate_redirect_go__book_key__get","parameters":[{"name":"book_key","in":"path","required":true,"schema":{"type":"string","title":"Book Key"}},{"name":"utm_source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Where the click came from: ai-claude, ai-chatgpt, partners-page, response-inline, etc","title":"Utm Source"},"description":"Where the click came from: ai-claude, ai-chatgpt, partners-page, response-inline, etc"},{"name":"utm_campaign","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Utm Campaign"}},{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"US","title":"Country"}},{"name":"state","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"State"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/partners":{"get":{"summary":"Partners Page","description":"Public partners page. Lists every active affiliate book with a\nbonus tile users can click. Uses /go/{book_key} for redirect so we\nget attribution.","operationId":"partners_page_partners_get","parameters":[{"name":"country","in":"query","required":false,"schema":{"type":"string","default":"US","title":"Country"}}],"responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/terms":{"get":{"summary":"Terms Of Service","operationId":"terms_of_service_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/tos":{"get":{"summary":"Terms Of Service","operationId":"terms_of_service_legal_tos_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/terms":{"get":{"summary":"Terms Of Service","operationId":"terms_of_service_legal_terms_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/privacy":{"get":{"summary":"Privacy Policy","operationId":"privacy_policy_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/privacy":{"get":{"summary":"Privacy Policy","operationId":"privacy_policy_legal_privacy_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/acceptable-use":{"get":{"summary":"Acceptable Use Policy","operationId":"acceptable_use_policy_acceptable_use_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/aup":{"get":{"summary":"Acceptable Use Policy","operationId":"acceptable_use_policy_legal_aup_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/acceptable-use":{"get":{"summary":"Acceptable Use Policy","operationId":"acceptable_use_policy_legal_acceptable_use_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/dmca":{"get":{"summary":"Dmca Policy","operationId":"dmca_policy_dmca_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal/dmca":{"get":{"summary":"Dmca Policy","operationId":"dmca_policy_legal_dmca_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/legal":{"get":{"summary":"Legal Index","operationId":"legal_index_legal_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/v1/sandbox/sports":{"get":{"summary":"Sandbox Sports","description":"Fake /v1/sports response. Same shape as real endpoint, deterministic.\nNo auth, IP rate-limited.","operationId":"sandbox_sports_v1_sandbox_sports_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/sandbox/sports/{sport_key}/odds":{"get":{"summary":"Sandbox Odds","description":"Fake /v1/sports/{sport_key}/odds response. Returns 3 synthetic\ngames for the requested sport. Real /v1/odds will return the actual\ngames for that sport, including 0 if none are scheduled.","operationId":"sandbox_odds_v1_sandbox_sports__sport_key__odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"regions","in":"query","required":false,"schema":{"type":"string","default":"us","title":"Regions"}},{"name":"markets","in":"query","required":false,"schema":{"type":"string","default":"h2h","title":"Markets"}},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmakers"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/props":{"get":{"summary":"Sandbox Props","description":"Sandbox player-prop response. Returns deterministic synthetic\nprop rows for the requested sport so integrators can verify parsing\nof the real /v1/sports/{sport_key}/props shape without spending\ncredits.\n\nNo auth. IP rate-limited at 60 req/min. iter_042 #354.","operationId":"sandbox_props_v1_sandbox_sports__sport_key__props_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of prop market keys","title":"Markets"},"description":"CSV of prop market keys"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of book keys (filter applied to output)","title":"Bookmakers"},"description":"CSV of book keys (filter applied to output)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/live/period_markets":{"get":{"summary":"Sandbox Period Markets","description":"Fake period markets response. Useful for verifying the period\nmarkets integration shape.","operationId":"sandbox_period_markets_v1_sandbox_sports__sport_key__live_period_markets_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"period","in":"query","required":false,"schema":{"type":"string","default":"all","title":"Period"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/live/sse":{"get":{"summary":"Sandbox Sse","description":"Fake SSE stream. Pushes a synthetic state-change event every 3\nseconds so prospects can verify SSE integration before paying.\nSelf-terminates after 30 events to bound resource use.","operationId":"sandbox_sse_v1_sandbox_sports__sport_key__live_sse_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/arbitrage":{"get":{"summary":"Sandbox Arbitrage","description":"Sandbox arbitrage response. Deterministic synthetic 2-side\ncross-book arbs. iter_068 #491.","operationId":"sandbox_arbitrage_v1_sandbox_sports__sport_key__arbitrage_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/ev":{"get":{"summary":"Sandbox Ev","description":"Sandbox +EV response. iter_068 #491.","operationId":"sandbox_ev_v1_sandbox_sports__sport_key__ev_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/consensus":{"get":{"summary":"Sandbox Consensus","description":"Sandbox consensus response. iter_068 #491.","operationId":"sandbox_consensus_v1_sandbox_sports__sport_key__consensus_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/futures":{"get":{"summary":"Sandbox Futures","description":"Sandbox futures response. iter_068 #491.","operationId":"sandbox_futures_v1_sandbox_sports__sport_key__futures_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/scores":{"get":{"summary":"Sandbox Scores","description":"Sandbox scores response. iter_068 #491.","operationId":"sandbox_scores_v1_sandbox_sports__sport_key__scores_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sandbox/sports/{sport_key}/events":{"get":{"summary":"Sandbox Events","description":"Sandbox events response. iter_068 #491.","operationId":"sandbox_events_v1_sandbox_sports__sport_key__events_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/event-markets/examples":{"get":{"tags":["event_markets"],"summary":"Event Market Examples","description":"Return copy-paste example searches for event-market discovery.","operationId":"event_market_examples_v1_event_markets_examples_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/prediction-markets/search":{"get":{"tags":["event_markets"],"summary":"Search Event Markets","description":"Search non-standard event markets across public/free source surfaces.\n\nThis is a beta discovery endpoint. It does not assert arbitrage-ready\nequivalence across venues, it returns candidate matches with source-native\nprices so customers can inspect the exact live markets.","operationId":"search_event_markets_v1_prediction_markets_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":120,"description":"Free-text market search","title":"Q"},"description":"Free-text market search"},{"name":"sources","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated sources","default":"kalshi,polymarket,novig","title":"Sources"},"description":"Comma-separated sources"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":25,"title":"Limit"}},{"name":"min_volume","in":"query","required":false,"schema":{"type":"number","minimum":0,"description":"Drop markets below this source-native volume","default":0,"title":"Min Volume"},"description":"Drop markets below this source-native volume"},{"name":"min_confidence","in":"query","required":false,"schema":{"type":"number","maximum":1,"minimum":0,"description":"Drop weak text matches","default":0,"title":"Min Confidence"},"description":"Drop weak text matches"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","description":"balanced or match","default":"balanced","title":"Sort"},"description":"balanced or match"},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Raw"}},{"name":"include_closed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Closed"}},{"name":"include_unpriced","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Unpriced"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/event-markets/search":{"get":{"tags":["event_markets"],"summary":"Search Event Markets","description":"Search non-standard event markets across public/free source surfaces.\n\nThis is a beta discovery endpoint. It does not assert arbitrage-ready\nequivalence across venues, it returns candidate matches with source-native\nprices so customers can inspect the exact live markets.","operationId":"search_event_markets_v1_event_markets_search_get","parameters":[{"name":"q","in":"query","required":true,"schema":{"type":"string","minLength":2,"maxLength":120,"description":"Free-text market search","title":"Q"},"description":"Free-text market search"},{"name":"sources","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated sources","default":"kalshi,polymarket,novig","title":"Sources"},"description":"Comma-separated sources"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":50,"minimum":1,"default":25,"title":"Limit"}},{"name":"min_volume","in":"query","required":false,"schema":{"type":"number","minimum":0,"description":"Drop markets below this source-native volume","default":0,"title":"Min Volume"},"description":"Drop markets below this source-native volume"},{"name":"min_confidence","in":"query","required":false,"schema":{"type":"number","maximum":1,"minimum":0,"description":"Drop weak text matches","default":0,"title":"Min Confidence"},"description":"Drop weak text matches"},{"name":"sort","in":"query","required":false,"schema":{"type":"string","description":"balanced or match","default":"balanced","title":"Sort"},"description":"balanced or match"},{"name":"include_raw","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Raw"}},{"name":"include_closed","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Closed"}},{"name":"include_unpriced","in":"query","required":false,"schema":{"type":"boolean","default":false,"title":"Include Unpriced"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/calc/kelly":{"get":{"summary":"Calc Kelly","description":"Fractional Kelly stake. FREE. iter-75 deploy / #456.\n\nReturns the optimal stake size given a bankroll, the price you can\nbet at, your estimated win probability, and a fraction-of-Kelly\nmultiplier (most bettors use 0.25-0.5 Kelly to avoid overbetting\non noisy win-prob estimates).\n\n`expected_value_usd` is the +EV in dollars (positive means the bet\nis +EV at your win_prob; negative means -EV). If the Kelly stake\nis negative, the math says don't bet.","operationId":"calc_kelly_v1_calc_kelly_get","parameters":[{"name":"bankroll","in":"query","required":true,"schema":{"type":"number","exclusiveMinimum":0,"description":"Total bankroll in USD","title":"Bankroll"},"description":"Total bankroll in USD"},{"name":"odds","in":"query","required":true,"schema":{"type":"string","description":"Bet price (American or decimal, e.g. '-110' or '1.91')","title":"Odds"},"description":"Bet price (American or decimal, e.g. '-110' or '1.91')"},{"name":"win_prob","in":"query","required":true,"schema":{"type":"number","exclusiveMaximum":1,"exclusiveMinimum":0,"description":"Your estimated win probability (0 < p < 1)","title":"Win Prob"},"description":"Your estimated win probability (0 < p < 1)"},{"name":"fraction","in":"query","required":false,"schema":{"type":"number","maximum":1.0,"exclusiveMinimum":0,"description":"Kelly fraction multiplier (0.25 = quarter Kelly)","default":0.25,"title":"Fraction"},"description":"Kelly fraction multiplier (0.25 = quarter Kelly)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/calc/hedge":{"get":{"summary":"Calc Hedge","description":"Compute the hedge stake. FREE. iter-75 deploy / #456.\n\nTwo-outcome hedge against an existing position. `original_stake` is\nwhat you placed on side A at `original_odds`. `hedge_odds` is what\nthe OTHER side is currently quoted at. Returns the hedge stake +\nprofit-if-A-wins and profit-if-B-wins.","operationId":"calc_hedge_v1_calc_hedge_get","parameters":[{"name":"original_stake","in":"query","required":true,"schema":{"type":"number","exclusiveMinimum":0,"description":"Stake you already placed","title":"Original Stake"},"description":"Stake you already placed"},{"name":"original_odds","in":"query","required":true,"schema":{"type":"string","description":"Odds you took (American or decimal)","title":"Original Odds"},"description":"Odds you took (American or decimal)"},{"name":"hedge_odds","in":"query","required":true,"schema":{"type":"string","description":"Current available odds on the other side","title":"Hedge Odds"},"description":"Current available odds on the other side"},{"name":"target","in":"query","required":false,"schema":{"type":"string","description":"One of: equal_profit (lock in identical profit either side), guaranteed_minimum (max guaranteed return), free_roll (bet just enough to recover original_stake)","default":"equal_profit","title":"Target"},"description":"One of: equal_profit (lock in identical profit either side), guaranteed_minimum (max guaranteed return), free_roll (bet just enough to recover original_stake)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/calc/edge":{"get":{"summary":"Calc Edge","description":"+EV / fair-line / no-vig calculator. FREE. iter-75 deploy / #456.\n\nTwo modes:\n  1. Pass `true_prob` directly: returns EV vs your stated win prob.\n  2. Pass `sharp_over_odds` + `sharp_under_odds`: derives the no-vig\n     fair probability from the two-sided sharp market, then computes\n     EV against that fair line. This is the canonical EV-scan formula.\n\n`edge_pct` is in percent (positive = +EV).","operationId":"calc_edge_v1_calc_edge_get","parameters":[{"name":"odds","in":"query","required":true,"schema":{"type":"string","description":"The price you can bet at (American or decimal)","title":"Odds"},"description":"The price you can bet at (American or decimal)"},{"name":"true_prob","in":"query","required":false,"schema":{"anyOf":[{"type":"number","exclusiveMaximum":1,"exclusiveMinimum":0},{"type":"null"}],"description":"Your estimated true win probability. Either this OR (sharp_over_odds + sharp_under_odds) required.","title":"True Prob"},"description":"Your estimated true win probability. Either this OR (sharp_over_odds + sharp_under_odds) required."},{"name":"sharp_over_odds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sharp book's price on the SAME side as `odds`. Used with sharp_under_odds for no-vig fair-line derivation.","title":"Sharp Over Odds"},"description":"Sharp book's price on the SAME side as `odds`. Used with sharp_under_odds for no-vig fair-line derivation."},{"name":"sharp_under_odds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Sharp book's price on the OPPOSITE side. Used with sharp_over_odds for no-vig.","title":"Sharp Under Odds"},"description":"Sharp book's price on the OPPOSITE side. Used with sharp_over_odds for no-vig."},{"name":"stake","in":"query","required":false,"schema":{"type":"number","exclusiveMinimum":0,"description":"Stake to compute EV-in-dollars (default $100)","default":100,"title":"Stake"},"description":"Stake to compute EV-in-dollars (default $100)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/calc/free-bet":{"get":{"summary":"Calc Free Bet","description":"Convert a sportsbook free-bet promo to guaranteed cash. FREE.\niter-75 deploy / #456.\n\nThe free-bet pays out as STAKE-FREE-WINNINGS (the original stake is\nNOT returned). So on a $50 free bet at +200, win → $100 returned\n(not $150). Hedge math accounts for this: hedge_stake = free_bet * b\nwhere b = bet_odds_decimal - 1.","operationId":"calc_free_bet_v1_calc_free_bet_get","parameters":[{"name":"free_bet_usd","in":"query","required":true,"schema":{"type":"number","exclusiveMinimum":0,"description":"Face value of the free bet","title":"Free Bet Usd"},"description":"Face value of the free bet"},{"name":"bet_odds","in":"query","required":true,"schema":{"type":"string","description":"Odds you'd take on side A using the free bet","title":"Bet Odds"},"description":"Odds you'd take on side A using the free bet"},{"name":"hedge_odds","in":"query","required":true,"schema":{"type":"string","description":"Odds on side B at a different book for the hedge","title":"Hedge Odds"},"description":"Odds on side B at a different book for the hedge"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/middles":{"get":{"summary":"Find Middles","description":"Find spread/total middle opportunities across books. iter-75 deploy / #455.\n\n3 credits.\n\nA \"middle\" is when one book offers Over X and another book offers Under Y\nwith X < Y; if the game lands between X and Y, BOTH bets cash. Same idea\nfor spreads (one book's +6, another's -5 — if the favorite wins by exactly\n5, both bets cash).\n\nReturns one row per (event, market) where the highest-Over line is below\nthe lowest-Under line (totals) or symmetric for spreads. The `gap` field\nis the size of the middle window in points.","operationId":"find_middles_v1_sports__sport_key__middles_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"min_gap","in":"query","required":false,"schema":{"type":"number","maximum":10.0,"minimum":0.0,"description":"Minimum point gap between the two sides to count as a middle (default 0.5)","default":0.5,"title":"Min Gap"},"description":"Minimum point gap between the two sides to count as a middle (default 0.5)"},{"name":"min_books","in":"query","required":false,"schema":{"type":"integer","minimum":2,"description":"Minimum distinct books required to flag a middle (default 2)","default":2,"title":"Min Books"},"description":"Minimum distinct books required to flag a middle (default 2)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sse/odds/{sport_key}":{"get":{"summary":"Sse Odds","description":"Pro+ tier SSE endpoint. Same data feed as /ws/odds, just streamed\nas text/event-stream so corporate proxies / browser EventSource /\nHTTP-only environments work without a WebSocket upgrade.\n\nEach event is a single JSON line in the standard SSE shape:\n    data: {\"type\": \"odds_update\", \"sport_key\": \"...\", ...}\\n\\n\n\nHeartbeat every 5s by default to prove the stream is live through\ncorporate proxies and browser EventSource clients. Initial state and\nsource freshness are sent immediately on connect.","operationId":"sse_odds_v1_sse_odds__sport_key__get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","title":"Apikey"}},{"name":"event_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional: filter to one game's event_id","title":"Event Id"},"description":"Optional: filter to one game's event_id"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated books, e.g. fanduel,pinnacle","title":"Bookmakers"},"description":"Comma-separated books, e.g. fanduel,pinnacle"},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated prop market keys, e.g. player_points,player_rebounds","title":"Markets"},"description":"Comma-separated prop market keys, e.g. player_points,player_rebounds"},{"name":"kinds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated row kinds: game,prop","title":"Kinds"},"description":"Comma-separated row kinds: game,prop"},{"name":"heartbeat_s","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"description":"SSE heartbeat seconds","default":5,"title":"Heartbeat S"},"description":"SSE heartbeat seconds"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Initial snapshot row limit","default":500,"title":"Limit"},"description":"Initial snapshot row limit"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sse/hot/{sport_key}":{"get":{"summary":"Sse Odds","description":"Pro+ tier SSE endpoint. Same data feed as /ws/odds, just streamed\nas text/event-stream so corporate proxies / browser EventSource /\nHTTP-only environments work without a WebSocket upgrade.\n\nEach event is a single JSON line in the standard SSE shape:\n    data: {\"type\": \"odds_update\", \"sport_key\": \"...\", ...}\\n\\n\n\nHeartbeat every 5s by default to prove the stream is live through\ncorporate proxies and browser EventSource clients. Initial state and\nsource freshness are sent immediately on connect.","operationId":"sse_odds_v1_sse_hot__sport_key__get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","title":"Apikey"}},{"name":"event_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional: filter to one game's event_id","title":"Event Id"},"description":"Optional: filter to one game's event_id"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated books, e.g. fanduel,pinnacle","title":"Bookmakers"},"description":"Comma-separated books, e.g. fanduel,pinnacle"},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated prop market keys, e.g. player_points,player_rebounds","title":"Markets"},"description":"Comma-separated prop market keys, e.g. player_points,player_rebounds"},{"name":"kinds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated row kinds: game,prop","title":"Kinds"},"description":"Comma-separated row kinds: game,prop"},{"name":"heartbeat_s","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"description":"SSE heartbeat seconds","default":5,"title":"Heartbeat S"},"description":"SSE heartbeat seconds"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":1000,"minimum":1,"description":"Initial snapshot row limit","default":500,"title":"Limit"},"description":"Initial snapshot row limit"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/odds-drop/{sport_key}":{"get":{"summary":"Sse Odds Drop","description":"Business+ tier SSE endpoint that filters the broadcast stream to\nline-MOVES only. Customer configures a threshold (in American-odds\ncents) and we push an event the moment a tracked side crosses it.\n\nEach event has shape:\n    data: {\"type\":\"odds_drop\",\"event_id\":\"...\",\"bookmaker\":\"...\",\n           \"side\":\"h2h_home\",\"prev\":-110,\"new\":-120,\"delta\":-10,\n           \"direction\":\"toward_favorite\",\"timestamp\":1747...}\\n\\n\n\nSingle-sport subscription:\n    GET /v1/odds-drop/baseball_mlb?apiKey=...\n\nMulti-sport subscription (one connection, many sport_keys):\n    GET /v1/odds-drop/all?apiKey=...\n    GET /v1/odds-drop/multi?apiKey=...&sports=baseball_mlb,basketball_nba\n\nThe wildcard 'all' value subscribes to every sport_key with broadcast\nactivity in the last 5 minutes (~100-200 sport keys at peak). Drops\ninclude a `sport_key` field so you can route them downstream.","operationId":"sse_odds_drop_v1_odds_drop__sport_key__get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"apiKey","in":"query","required":true,"schema":{"type":"string","title":"Apikey"}},{"name":"threshold","in":"query","required":false,"schema":{"type":"integer","maximum":200,"minimum":1,"description":"Minimum American-odds delta to trigger an event. Default 10 (a -110 becoming -120, or +100 becoming +110).","default":10,"title":"Threshold"},"description":"Minimum American-odds delta to trigger an event. Default 10 (a -110 becoming -120, or +100 becoming +110)."},{"name":"event_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Optional: filter to one game's event_id","title":"Event Id"},"description":"Optional: filter to one game's event_id"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated books","title":"Bookmakers"},"description":"Comma-separated books"},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated market keys","title":"Markets"},"description":"Comma-separated market keys"},{"name":"sports","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated sport_keys for multi-sport subscription. Overrides path sport_key. Use 'all' as a sentinel to subscribe to every sport_key with recent activity (last 5 min).","title":"Sports"},"description":"Comma-separated sport_keys for multi-sport subscription. Overrides path sport_key. Use 'all' as a sentinel to subscribe to every sport_key with recent activity (last 5 min)."},{"name":"direction","in":"query","required":false,"schema":{"type":"string","description":"both | toward_favorite | toward_dog. Filter by which way the line moved.","default":"both","title":"Direction"},"description":"both | toward_favorite | toward_dog. Filter by which way the line moved."},{"name":"heartbeat_s","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"default":5,"title":"Heartbeat S"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports":{"get":{"summary":"List Sports","description":"List available sports. FREE, no credits charged, no API key required.\n\nReturns 79 sports including MLB, NFL, NBA, NHL, MMA, Boxing, Cricket,\nesports, volleyball, table tennis, and 51 soccer leagues.\n\nSoccer sport keys follow the pattern: soccer_epl, soccer_germany_bundesliga, etc.","operationId":"list_sports_v1_sports_get","parameters":[{"name":"all","in":"query","required":false,"schema":{"type":"boolean","description":"Include inactive sports","default":false,"title":"All"},"description":"Include inactive sports"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/pricing":{"get":{"summary":"Get Pricing","description":"Public pricing endpoint. Returns the tier table for programmatic\nintegration (Stripe widgets, AI agents, comparison pages, the JSON\nside of the HTML /pricing page). No API key required.","operationId":"get_pricing_v1_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/regions":{"get":{"summary":"List Regions","description":"List supported `regions` filter values used by /v1/sports/{key}/odds.\nPublic, no auth, no credits. iter_062 #474.","operationId":"list_regions_v1_regions_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/markets":{"get":{"summary":"List Markets","description":"List supported `markets` filter values across the API.\nPublic, no auth, no credits. iter_062 #474.\n\nThe market_key vocabulary is large (60+ values) and grows over the\nseason as books add new prop markets. This endpoint returns the\ncurrent canonical set used by /odds, /props, /ev, /consensus, and\n/parlay/price.","operationId":"list_markets_v1_markets_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/bookmakers":{"get":{"summary":"List Bookmakers","description":"List supported bookmakers with their integration status.\n\nStatus values:\n  active                Currently ingested.\n  merged                Brand merged into another active bookmaker (see merged_into).\n  decommissioned        Bookmaker shut down or no longer publicly accessible.\n  not_yet_integrated    Known book we haven't wired up yet.\n\nDefaults to only active books. Pass ?all=true to see merged / decommissioned\nentries plus the explanation note for each.\n\nBy default each book carries an `endpoints` array (game_lines / live /\nprops / prediction / event_markets / historical) plus `example_paths`\nshowing how to query it. Pass `?include_endpoints=false` for the lean\ncatalog without those.","operationId":"list_bookmakers_v1_bookmakers_get","parameters":[{"name":"all","in":"query","required":false,"schema":{"type":"boolean","description":"Include non-active (merged/decommissioned) entries","default":false,"title":"All"},"description":"Include non-active (merged/decommissioned) entries"},{"name":"include_endpoints","in":"query","required":false,"schema":{"type":"boolean","description":"Attach endpoints[] + example_paths per book. Set false for the lean catalog.","default":true,"title":"Include Endpoints"},"description":"Attach endpoints[] + example_paths per book. Set false for the lean catalog."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/bookmakers/{key}":{"get":{"summary":"Get Bookmaker","description":"Return one bookmaker's catalog entry with endpoints + example URLs.\n\nReturns 404 with the full active list if `key` is unknown so callers\ncan recover by suggestion.","operationId":"get_bookmaker_v1_bookmakers__key__get","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/bookmakers/{key}/freshness":{"get":{"summary":"Get Bookmaker Freshness","description":"Live data-freshness diagnostic for one book.\n\nReturns the most recent timestamp this book wrote into each backing\ntable, plus row counts in the last hour and 24 hours. Useful when\ncallers want to verify \"is this book actually flowing\" without\npolling /odds and /props themselves.\n\nTables checked:\n  odds_snapshots          (game-line h2h/spreads/totals)\n  prop_snapshots          (player props + prediction markets)\n  period_odds_snapshots   (1H, Q1-Q4, halves, etc.)\n\nFree, no auth, no credits charged. Intended for status pages and\ncustomer health checks.","operationId":"get_bookmaker_freshness_v1_bookmakers__key__freshness_get","parameters":[{"name":"key","in":"path","required":true,"schema":{"type":"string","title":"Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/events":{"get":{"summary":"List Events","description":"List upcoming events. FREE - no credits charged.","operationId":"list_events_v1_sports__sport_key__events_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"dateFormat","in":"query","required":false,"schema":{"type":"string","default":"iso","title":"Dateformat"}},{"name":"eventIds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated event IDs","title":"Eventids"},"description":"Comma-separated event IDs"},{"name":"commenceTimeFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commencetimefrom"}},{"name":"commenceTimeTo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commencetimeto"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/participants":{"get":{"summary":"List Participants","description":"List teams or players for a sport in TOA participant shape. 1 credit.","operationId":"list_participants_v1_sports__sport_key__participants_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/events/canonical":{"get":{"summary":"List Canonical Events","description":"List events grouped by canonical ID across ALL sources.\n\nEach canonical event shows which sources have it and links their source-specific\nevent IDs + team name variations. Useful for joining data across books.\n\nCredits: 2","operationId":"list_canonical_events_v1_sports__sport_key__events_canonical_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/odds":{"get":{"summary":"Get Odds","description":"Get odds for upcoming and live events.\n\nCredits: markets_count x regions_count (same formula as the-odds-api).\n\n**79 sports supported** including 51 soccer leagues, esports, and volleyball.\n\n**Regions:** us, us2, uk, eu, au. Use eu for Pinnacle and European bookmakers.\n\n**Markets:** h2h (moneyline/3-way), spreads, totals.\n\n**Filter by bookmaker:** `?bookmakers=pinnacle,draftkings`\n\n**Live only:** `?live=true` returns events that have already started (same\ncost as a normal call, no extra charge). For a dedicated in-play endpoint\nthat exposes live-tagged player props alongside game lines, see\n`/v1/sports/{sport_key}/live`.\n\n**Slim payload:** `?include=slim` drops raw upstream JSON (~40% smaller).\n\n**Verified-at signal:** every bookmaker's `last_update` is the freshest of\n(price-change, no-change verification heartbeat). On hot-cycle sources\n(Pinnacle, FanDuel) that means a maximum age around the 2-second poll\ninterval, even if the price hasn't moved. Add `?include=verification` to\nalso receive:\n  - `verified_at`: the heartbeat timestamp (we polled and saw the same price)\n  - `line_changed_at`: the last actual price-move timestamp\n  - `is_current`: true iff verified_at is within the last 5 seconds\n\nCombine with shape tokens, e.g. `?include=slim,verification`.\n\n**Example:** `GET /v1/sports/soccer_epl/odds?regions=eu&markets=h2h,spreads&bookmakers=pinnacle`","operationId":"get_odds_v1_sports__sport_key__odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"regions","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated: us,us2,uk,eu,au. Default 'us'. iter_047 #420: was previously required (...). Made optional with 'us' default so consumers running pre-existing The-Odds-API-style code (which defaulted to us) don't 422.","default":"us","title":"Regions"},"description":"Comma-separated: us,us2,uk,eu,au. Default 'us'. iter_047 #420: was previously required (...). Made optional with 'us' default so consumers running pre-existing The-Odds-API-style code (which defaulted to us) don't 422."},{"name":"markets","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated: h2h,spreads,totals","default":"h2h","title":"Markets"},"description":"Comma-separated: h2h,spreads,totals"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"decimal","title":"Oddsformat"}},{"name":"dateFormat","in":"query","required":false,"schema":{"type":"string","default":"iso","title":"Dateformat"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated bookmaker keys (overrides regions)","title":"Bookmakers"},"description":"Comma-separated bookmaker keys (overrides regions)"},{"name":"eventIds","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated event IDs","title":"Eventids"},"description":"Comma-separated event IDs"},{"name":"commenceTimeFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commencetimefrom"}},{"name":"commenceTimeTo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Commencetimeto"}},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Shortcut: events whose commence_time falls on this UTC date (YYYY-MM-DD). Sugar for commenceTimeFrom=<date>T00:00:00Z and commenceTimeTo=<date>T23:59:59Z. Explicit commenceTime* values win.","title":"Date"},"description":"Shortcut: events whose commence_time falls on this UTC date (YYYY-MM-DD). Sugar for commenceTimeFrom=<date>T00:00:00Z and commenceTimeTo=<date>T23:59:59Z. Explicit commenceTime* values win."},{"name":"include","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated. Shape tokens: normalized (default), slim (drops raw_json), raw (ids + raw_json only). Add 'verification' to include verified_at, line_changed_at, and is_current per bookmaker.","default":"normalized","title":"Include"},"description":"Comma-separated. Shape tokens: normalized (default), slim (drops raw_json), raw (ids + raw_json only). Add 'verification' to include verified_at, line_changed_at, and is_current per bookmaker."},{"name":"live","in":"query","required":false,"schema":{"type":"boolean","description":"Live games only (commence_time at or before now). Equivalent to passing commenceTimeTo=<now>. No extra cost.","default":false,"title":"Live"},"description":"Live games only (commence_time at or before now). Equivalent to passing commenceTimeTo=<now>. No extra cost."},{"name":"include_live","in":"query","required":false,"schema":{"type":"boolean","description":"Include in-progress games in the response (off by default — /odds is for pregame analysis). Set true to get both pregame and live in one call.","default":false,"title":"Include Live"},"description":"Include in-progress games in the response (off by default — /odds is for pregame analysis). Set true to get both pregame and live in one call."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/events/{event_id}/odds":{"get":{"summary":"Get Event Odds","description":"Get odds for a single event.","operationId":"get_event_odds_v1_sports__sport_key__events__event_id__odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"event_id","in":"path","required":true,"schema":{"type":"string","title":"Event Id"}},{"name":"regions","in":"query","required":true,"schema":{"type":"string","title":"Regions"}},{"name":"markets","in":"query","required":false,"schema":{"type":"string","default":"h2h","title":"Markets"}},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"decimal","title":"Oddsformat"}},{"name":"dateFormat","in":"query","required":false,"schema":{"type":"string","default":"iso","title":"Dateformat"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated bookmaker keys. Overrides regions. Returns only the listed books that have data for this event+market.","title":"Bookmakers"},"description":"Comma-separated bookmaker keys. Overrides regions. Returns only the listed books that have data for this event+market."},{"name":"include","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated. 'verification' adds verified_at / line_changed_at / is_current per bookmaker.","default":"normalized","title":"Include"},"description":"Comma-separated. 'verification' adds verified_at / line_changed_at / is_current per bookmaker."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/scores":{"get":{"summary":"Get Scores","description":"Get live scores and recent results. 1-2 credits.\n\nCovers NHL, NBA, MLB, NFL, MMA/UFC, and major soccer leagues via ESPN.\nReturns live game state, scores, period/quarter/inning, and completion status.\n\n**Example:** `GET /v1/sports/icehockey_nhl/scores`","operationId":"get_scores_v1_sports__sport_key__scores_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"daysFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":14,"minimum":1},{"type":"null"}],"description":"Days of history (1-14). For longer windows use the historical endpoints.","title":"Daysfrom"},"description":"Days of history (1-14). For longer windows use the historical endpoints."},{"name":"dateFormat","in":"query","required":false,"schema":{"type":"string","default":"iso","title":"Dateformat"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/sports/{sport_key}/odds":{"get":{"summary":"Get Historical Odds","description":"Get historical odds at a point in time.\n\nCredits: 10 × markets × regions.\n\niter_058 #463 — `date=` vs `dateFrom=&dateTo=` return different rows\nfor the same date. The single-date path (`?date=YYYY-MM-DD`) reads\nfrom the long-term archive and includes the `_an` \"anchored\"\nclosing-line variants (e.g. `draftkings_an`, `fanduel_an`,\n`bet365_an`) alongside the regular bookmaker rows. The range path\n(`?dateFrom=...&dateTo=...`) reads from the rolling snapshot table\nand surfaces only the bare bookmaker keys (without `_an`).\nFor consistent backtest joins, prefer `?date=` when you need\nclosing-line anchors. See /v1/bookmakers/{key}.historical_variants\nfor the suffix convention.\n\n`date` omitted returns the latest snapshot (equivalent to current\n/v1/sports/{sport_key}/odds but routed through the historical\npricing). This matches the UX of /closing-odds which doesn't\nrequire an explicit date either - removes the 422-on-missing-param\ntrap that customers hit when they don't read the schema first.","operationId":"get_historical_odds_v1_historical_sports__sport_key__odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"ISO 8601 timestamp OR YYYY-MM-DD date. Omit to get the most recent snapshot. See doc note below for behavior.","title":"Date"},"description":"ISO 8601 timestamp OR YYYY-MM-DD date. Omit to get the most recent snapshot. See doc note below for behavior."},{"name":"regions","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated: us,us2,uk,eu,au. Default us.","default":"us","title":"Regions"},"description":"Comma-separated: us,us2,uk,eu,au. Default us."},{"name":"markets","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated: h2h,spreads,totals. Default h2h.","default":"h2h","title":"Markets"},"description":"Comma-separated: h2h,spreads,totals. Default h2h."},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"decimal","title":"Oddsformat"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/sports/{sport_key}/matches":{"get":{"summary":"Get Historical Matches","description":"Historical match/result archive.\n\nUse this when a source has real historical match data but not historical\nprices. Rows with actual odds include `has_odds=true` and an `odds` object.","operationId":"get_historical_matches_v1_historical_sports__sport_key__matches_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"dateFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date YYYY-MM-DD","title":"Datefrom"},"description":"Start date YYYY-MM-DD"},{"name":"dateTo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date YYYY-MM-DD","title":"Dateto"},"description":"End date YYYY-MM-DD"},{"name":"sources","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated sources, e.g. hltv,opendota,vlrgg,pinnacle","title":"Sources"},"description":"Comma-separated sources, e.g. hltv,opendota,vlrgg,pinnacle"},{"name":"pricedOnly","in":"query","required":false,"schema":{"type":"boolean","description":"Only rows that include real odds/prices","default":false,"title":"Pricedonly"},"description":"Only rows that include real odds/prices"},{"name":"includeRaw","in":"query","required":false,"schema":{"type":"boolean","description":"Include raw source payload","default":false,"title":"Includeraw"},"description":"Include raw source payload"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":5000,"minimum":1,"default":1000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/line-movement":{"get":{"summary":"Get Line Movement","description":"Track how odds move over time for an event. 2 credits.\n\nReturns time-series of odds snapshots for the specified event, showing\nline/price changes across bookmakers. Useful for CLV analysis and\nsteam detection.\n\n**Example:** `GET /v1/sports/baseball_mlb/line-movement?eventId=abc123&market=player_hits&player=Aaron Judge`","operationId":"get_line_movement_v1_sports__sport_key__line_movement_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"eventId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Eventid"}},{"name":"event_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Event Id"}},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Source"}},{"name":"bookmaker","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmaker"}},{"name":"market","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to market_key (e.g. player_points)","title":"Market"},"description":"Filter to market_key (e.g. player_points)"},{"name":"market_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Alias for market","title":"Market Key"},"description":"Alias for market"},{"name":"player","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to specific player","title":"Player"},"description":"Filter to specific player"},{"name":"hours","in":"query","required":false,"schema":{"type":"integer","description":"Lookback window in hours (max 168)","default":24,"title":"Hours"},"description":"Lookback window in hours (max 168)"},{"name":"window_minutes","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Lookback window in minutes (max 10080)","title":"Window Minutes"},"description":"Lookback window in minutes (max 10080)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/prediction-markets/{sport_key}":{"get":{"summary":"Get Prediction Markets","description":"Get prediction-market prices (Kalshi + Polymarket). 1 credit.\n\nReturns one row per (source, market) snapshot from the last hour.\nEXCLUSIVE: not available on the-odds-api.\n\nUse ?sources=kalshi or ?sources=polymarket to limit to one venue.\nFor free-text discovery across Kalshi, Polymarket, and Novig (no\ncredit charge), use /v1/event-markets/search instead.\n\nResponses are cached server-side for 30s; the `X-Cache` header is\n`HIT` or `MISS` so clients can see whether they hit Postgres.","operationId":"get_prediction_markets_v1_prediction_markets__sport_key__get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"sources","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated source keys. Supports kalshi, polymarket. Defaults to both.","default":"kalshi,polymarket","title":"Sources"},"description":"Comma-separated source keys. Supports kalshi, polymarket. Defaults to both."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/prediction-markets/crypto/{asset}":{"get":{"summary":"Get Crypto Prediction Markets","description":"Latest snapshot of every open Kalshi crypto prediction market\nfor the requested asset.\n\nAsset must be `btc`, `eth`, or `sol`. The headline product is BTC\n`direction_15m`: binary YES/NO contracts asking \"will BTC be higher\nin 15 minutes?\", resolving every quarter-hour 24/7.\n\nEach row shows the latest yes/no bid/ask + mid-market implied\nprobability + volume + open interest. Use the historical endpoint\n(`/v1/historical/prediction-markets/crypto/{asset}`) for replay.\n\nCost: 1 credit. Refreshes within ~5s of the underlying collector\npoll.","operationId":"get_crypto_prediction_markets_v1_prediction_markets_crypto__asset__get","parameters":[{"name":"asset","in":"path","required":true,"schema":{"type":"string","title":"Asset"}},{"name":"market_type","in":"query","required":false,"schema":{"type":"string","description":"Filter by market_type. One of: direction_15m, direction_1h, direction_daily, price_range, price_above, all. Default all.","default":"all","title":"Market Type"},"description":"Filter by market_type. One of: direction_15m, direction_1h, direction_daily, price_range, price_above, all. Default all."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":500,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/prediction-markets/crypto/{asset}":{"get":{"summary":"Get Crypto Prediction Markets Historical","description":"Tick-by-tick replay of Kalshi crypto prediction markets.\n\nEvery poll snapshot we captured is preserved here. The collector\npolls each open market every ~5 s, so a one-hour replay returns\n~720 rows per market.\n\nAsset must be `btc`, `eth`, or `sol`. Provide `market_ticker` to\nfocus on one resolution slot (e.g. `KXBTCD-26MAY13H1515` = BTC\nDirection resolving at 15:15 UTC on 2026-05-13).\n\nCost: 2 credits.","operationId":"get_crypto_prediction_markets_historical_v1_historical_prediction_markets_crypto__asset__get","parameters":[{"name":"asset","in":"path","required":true,"schema":{"type":"string","title":"Asset"}},{"name":"market_type","in":"query","required":false,"schema":{"type":"string","description":"Same as live endpoint: direction_15m / direction_1h / direction_daily / price_range / price_above / all","default":"all","title":"Market Type"},"description":"Same as live endpoint: direction_15m / direction_1h / direction_daily / price_range / price_above / all"},{"name":"market_ticker","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Replay a single Kalshi market_ticker (e.g. KXBTCD-26MAY13H1515).","title":"Market Ticker"},"description":"Replay a single Kalshi market_ticker (e.g. KXBTCD-26MAY13H1515)."},{"name":"from","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Start timestamp (unix ms). Defaults to 6 hours ago.","title":"From"},"description":"Start timestamp (unix ms). Defaults to 6 hours ago."},{"name":"to","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"End timestamp (unix ms). Defaults to now.","title":"To"},"description":"End timestamp (unix ms). Defaults to now."},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":2000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/props":{"get":{"summary":"Get Props","description":"Get player prop odds from 10+ sources. 3 credits.\n\nReal-time player props updated every 60-90 seconds from:\nDraftKings, FanDuel, Caesars, Bovada, Pinnacle, Fliff (real American odds),\nPrizePicks, Underdog, Betr, Pick6, Sleeper, Novig, ProphetX, Polymarket\n(event markets, opt-in via include_event_markets=true).\n\n**Supported sports:** NFL, NBA, MLB, NHL, MMA/UFC, Soccer, Tennis, Golf, and more.\n\n**Market keys:** player_points, player_rebounds, player_assists, player_three_pointers,\nplayer_strikeouts, player_hits, player_home_runs, player_total_bases, player_runs,\nplayer_rbis, player_goals, player_shots_on_goal, player_pts_rebs_asts, and 50+ more.\n\n**Example:** `GET /v1/sports/basketball_nba/props?markets=player_points,player_rebounds`\n\n**Filter by player:** `?player=LeBron`\n\n**Filter by book:** `?bookmakers=fliff,pinnacle`\n\n**Polymarket / Kalshi event markets:** `?bookmakers=polymarket` (auto-includes\nevent markets) or `?include_event_markets=true` (any source). For a dedicated\nprediction-market endpoint with question text + event_url, use\n`/v1/prediction-markets/{sport_key}`.\n\nReturns over/under lines with American odds from each source.","operationId":"get_props_v1_sports__sport_key__props_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated prop market keys (e.g. player_pass_yds,player_points)","title":"Markets"},"description":"Comma-separated prop market keys (e.g. player_pass_yds,player_points)"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated bookmaker keys","title":"Bookmakers"},"description":"Comma-separated bookmaker keys"},{"name":"player","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by player name (partial match)","title":"Player"},"description":"Filter by player name (partial match)"},{"name":"eventId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event ID","title":"Eventid"},"description":"Filter by event ID"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}},{"name":"dfsOdds","in":"query","required":false,"schema":{"type":"string","description":"DFS normalization: 'midpoint' = +100/-100 (default, zero-vig), 'effective' = per-book implied (PrizePicks/Underdog = -137/-137)","default":"midpoint","title":"Dfsodds"},"description":"DFS normalization: 'midpoint' = +100/-100 (default, zero-vig), 'effective' = per-book implied (PrizePicks/Underdog = -137/-137)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"description":"Max rows returned (default 5000, max 10000)","default":5000,"title":"Limit"},"description":"Max rows returned (default 5000, max 10000)"},{"name":"offset","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":0,"description":"Page offset within the result set. Combine with limit for pagination. For results past 10000 rows, narrow via ?markets= or ?bookmakers= filters instead.","default":0,"title":"Offset"},"description":"Page offset within the result set. Combine with limit for pagination. For results past 10000 rows, narrow via ?markets= or ?bookmakers= filters instead."},{"name":"grouped","in":"query","required":false,"schema":{"type":"boolean","description":"Return one entry per prop with a books[] array (recommended) instead of one row per book","default":false,"title":"Grouped"},"description":"Return one entry per prop with a books[] array (recommended) instead of one row per book"},{"name":"include_event_markets","in":"query","required":false,"schema":{"type":"boolean","description":"Include futures and prediction-market rows that lack a single home/away_team (Polymarket yes/no questions, Underdog season-longs, etc). Auto-enabled when bookmakers includes 'polymarket'.","default":false,"title":"Include Event Markets"},"description":"Include futures and prediction-market rows that lack a single home/away_team (Polymarket yes/no questions, Underdog season-longs, etc). Auto-enabled when bookmakers includes 'polymarket'."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/props/coverage":{"get":{"summary":"Get Props Coverage","description":"Show which fresh books survive the exact /props request filters.\n\nThis endpoint is for support diagnostics and does not charge credits.","operationId":"get_props_coverage_v1_sports__sport_key__props_coverage_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated prop market keys","title":"Markets"},"description":"Comma-separated prop market keys"},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated bookmaker keys","title":"Bookmakers"},"description":"Comma-separated bookmaker keys"},{"name":"player","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by player name","title":"Player"},"description":"Filter by player name"},{"name":"eventId","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by event ID","title":"Eventid"},"description":"Filter by event ID"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}},{"name":"dfsOdds","in":"query","required":false,"schema":{"type":"string","default":"midpoint","title":"Dfsodds"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":10000,"minimum":1,"default":5000,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/odds/coverage":{"get":{"summary":"Get Odds Coverage","description":"Per-book game-line coverage for this sport: which books have fresh\nh2h / spread / total prices, how many games each, and the freshest\ntimestamp per book.\n\nUse this before hitting /odds to check whether the books you care\nabout are actually live for the sport you're requesting. Diagnostic\nendpoint, no credit charge.","operationId":"get_odds_coverage_v1_sports__sport_key__odds_coverage_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"fresh_within_seconds","in":"query","required":false,"schema":{"type":"integer","maximum":86400,"minimum":60,"description":"Only count books with at least one update within this window.","default":900,"title":"Fresh Within Seconds"},"description":"Only count books with at least one update within this window."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/pinnacle-coverage":{"get":{"summary":"Pinnacle Coverage","description":"**Public, no-auth.** Pinnacle presence/absence per sport_key.\n\nFor every sport we track, shows whether Pinnacle has fresh prices\nand how stale the latest capture is. Use this to verify Pinnacle\nis actually live for the sports you're betting before committing\nto a backtest or arb pipeline that anchors on Pinnacle.\n\nCached 5 min. iter_049 #424.","operationId":"pinnacle_coverage_v1_pinnacle_coverage_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/status":{"get":{"summary":"Status Json","description":"**Public, no-auth.** Live endpoint health: per-source freshness,\nrequest-rate-log p50/p95, total request count last hour.\n\nSame data the /status page renders. Refreshes every 30 seconds.","operationId":"status_json_v1_status_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/sports/{sport_key}/props/markets":{"get":{"summary":"List Prop Markets","description":"List available prop market keys for a sport. FREE.\n\nReturns all prop market types we have data for (e.g. player_points, player_pass_yds).\nUse these keys with the /props endpoint's markets parameter.","operationId":"list_prop_markets_v1_sports__sport_key__props_markets_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/sports/{sport_key}/closing-odds":{"get":{"summary":"Get Historical Closing Odds","description":"Historical closing lines: game-line h2h/spreads/totals plus player props. 10 credits.\n\nGame lines come from the historical_odds archive (1.3M+ rows across 267 sport keys,\nsources include Pinnacle, bet365, Betway, William Hill, Interwetten, Ladbrokes,\nBetVictor, betfair_exchange).\n\nPlayer props come from prop_closing_lines (15.2M+ rows across 993 sport keys, all\nstandard player markets). Use `markets=player_strikeouts` (or any other player_*\nkey) to pull these. The `player=` param filters to a specific player by substring.\n\nPass `include_imports=true` to also union your own customer-imported rows\nfrom `POST /v1/historical/closing-lines/import` (cost: 0 extra credits).\n\nExamples:\n\n- `GET /v1/historical/sports/baseball_mlb/closing-odds?markets=player_strikeouts&date=2025-08-15&player=Skenes`\n- `GET /v1/historical/sports/soccer_epl/closing-odds?bookmakers=pinnacle&season=2023-24`\n- `GET /v1/historical/sports/basketball_nba/closing-odds?markets=h2h,player_points&dateFrom=2024-12-01&dateTo=2024-12-31&include_imports=true`","operationId":"get_historical_closing_odds_v1_historical_sports__sport_key__closing_odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"markets","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated. Game lines: h2h, spreads, totals. Player props: player_strikeouts, player_total_bases, player_points, player_rebounds, player_assists, player_pass_yds, player_rush_yds, player_shots_on_goal, etc. (any market_key from prop_closing_lines). Mix freely: markets=h2h,player_strikeouts.","default":"h2h","title":"Markets"},"description":"Comma-separated. Game lines: h2h, spreads, totals. Player props: player_strikeouts, player_total_bases, player_points, player_rebounds, player_assists, player_pass_yds, player_rush_yds, player_shots_on_goal, etc. (any market_key from prop_closing_lines). Mix freely: markets=h2h,player_strikeouts."},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Comma-separated. Game-line queries default to pinnacle. Prop-only queries default to all tracked books.","title":"Bookmakers"},"description":"Comma-separated. Game-line queries default to pinnacle. Prop-only queries default to all tracked books."},{"name":"season","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Season filter for game lines (e.g. 2023-24)","title":"Season"},"description":"Season filter for game lines (e.g. 2023-24)"},{"name":"date","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Specific date YYYY-MM-DD (shortcut for dateFrom=dateTo=date)","title":"Date"},"description":"Specific date YYYY-MM-DD (shortcut for dateFrom=dateTo=date)"},{"name":"dateFrom","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Start date (YYYY-MM-DD)","title":"Datefrom"},"description":"Start date (YYYY-MM-DD)"},{"name":"dateTo","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"End date (YYYY-MM-DD)","title":"Dateto"},"description":"End date (YYYY-MM-DD)"},{"name":"player","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter props to a specific player name (substring match)","title":"Player"},"description":"Filter props to a specific player name (substring match)"},{"name":"include_imports","in":"query","required":false,"schema":{"type":"boolean","description":"Also include rows you've imported via POST /v1/historical/closing-lines/import","default":false,"title":"Include Imports"},"description":"Also include rows you've imported via POST /v1/historical/closing-lines/import"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/closing-lines/import":{"post":{"summary":"Import Customer Closing Lines","description":"Bring-your-own historical closing lines (Pro tier+).\n\nStores rows in a per-customer namespace (customer_imported_closing_lines\ntable, isolated by your api_key). **The public historical archive\n(historical_odds) is read-only via this endpoint** — you can only\nsee / delete YOUR OWN imports. Other customers' imports are\ninvisible to you and yours to them.\n\nQuery your imports back through the standard\n`GET /v1/historical/sports/{sport_key}/closing-odds?include_imports=true`\nendpoint.\n\nCost: 1 credit per 1,000 rows submitted (rounded up).\nIdempotent: re-submitting the same row is a no-op via ON CONFLICT DO NOTHING.\n\nTier gate: Pro+ (iteration_019 #197 — free-tier was accepted at\nauth but the use case is paid-customer-only, so we gate explicitly).\n\nExample body:\n\n```\n[\n  {\n    \"game_date\": \"2025-08-15\",\n    \"sport_key\": \"baseball_mlb\",\n    \"home_team\": \"Detroit Tigers\",\n    \"away_team\": \"Texas Rangers\",\n    \"source\": \"fanduel\",\n    \"player_name\": \"Tarik Skubal\",\n    \"market_key\": \"player_strikeouts\",\n    \"line\": 7.5,\n    \"over_price\": -118,\n    \"under_price\": -102\n  }\n]\n```","operationId":"import_customer_closing_lines_v1_historical_closing_lines_import_post","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"array","items":{"type":"object","additionalProperties":true},"description":"Array of closing-line rows. Each row must have at minimum: game_date (YYYY-MM-DD), sport_key, home_team, away_team, source, market_key, line. Optional: commence_time, player_name, market_label, over_price, under_price, raw_json.","title":"Rows"}}}},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}},"delete":{"summary":"Delete Customer Closing Imports","description":"Delete YOUR OWN imported closing-line rows.\n\nScoped to the customer_imported_closing_lines table, filtered by\nyour api_key — this endpoint **cannot touch the public historical\narchive** (historical_odds is read-only via the API).\n\nWithout `sport_key`, deletes every row YOU'VE imported. With it,\nonly your rows for that sport. Always requires `confirm=true`.\n\nTier gate: Pro+ (matches the import sibling — iteration_019 #198).","operationId":"delete_customer_closing_imports_v1_historical_closing_lines_import_delete","parameters":[{"name":"sport_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to a sport_key (optional)","title":"Sport Key"},"description":"Filter to a sport_key (optional)"},{"name":"confirm","in":"query","required":false,"schema":{"type":"boolean","description":"Must be true to actually delete","default":false,"title":"Confirm"},"description":"Must be true to actually delete"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/coverage":{"get":{"summary":"Historical Coverage","description":"**Public, no-auth.** Cross-source historical odds coverage stats:\ntotal rows, span, sources per sport, score-coverage percentage.\nUse this to verify the archive is rich enough for your backtest\nbefore committing to a paid tier.","operationId":"historical_coverage_v1_historical_coverage_get","parameters":[{"name":"sport_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter the by_sport array + summary to a single sport. iter_062 #476.","title":"Sport Key"},"description":"Filter the by_sport array + summary to a single sport. iter_062 #476."},{"name":"source","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter the by_source array + summary to a single source. iter_062 #476.","title":"Source"},"description":"Filter the by_source array + summary to a single source. iter_062 #476."},{"name":"min_rows","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","minimum":1},{"type":"null"}],"description":"Drop by_sport / by_source entries with fewer than this many rows. iter_062 #476.","title":"Min Rows"},"description":"Drop by_sport / by_source entries with fewer than this many rows. iter_062 #476."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/historical/stats":{"get":{"summary":"Get Historical Stats","description":"Public endpoint. Stats about our historical odds archive.","operationId":"get_historical_stats_v1_historical_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/sports/{sport_key}/closing-lines":{"get":{"summary":"Get Closing Lines","description":"Get closing lines (last odds before match start). 5 credits.\n\nEXCLUSIVE. Returns the final odds snapshot for each bookmaker before each\nmatch commenced. Essential for CLV (closing line value) analysis.\n\nSupports all 79 sports. Pinnacle closing lines available for all 51 soccer leagues.\n\n**Bookmakers:** pinnacle (default), draftkings, fanduel, betmgm, caesars, bovada, and more.\n\n**Example:** `GET /v1/sports/soccer_epl/closing-lines?bookmakers=pinnacle&daysFrom=7`\n\nResponse includes h2h (3-way with draw for soccer), spreads, and totals.\nEach event includes the bookmaker's last update timestamp before kickoff.","operationId":"get_closing_lines_v1_sports__sport_key__closing_lines_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmakers"}},{"name":"daysFrom","in":"query","required":false,"schema":{"type":"integer","maximum":30,"minimum":1,"default":3,"title":"Daysfrom"}},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/futures":{"get":{"summary":"Get Futures","description":"Get futures/outrights odds (championship winners, MVP, etc). 5 credits.\n\nReturns long-term markets like championship winners, division winners,\nMVP awards, and season-long props.\n\n**Example:** `GET /v1/sports/icehockey_nhl/futures`","operationId":"get_futures_v1_sports__sport_key__futures_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmakers"}},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","description":"american (default) | decimal. Selections get a matching `decimal` field alongside the existing `american` keys when decimal is requested.","default":"american","title":"Oddsformat"},"description":"american (default) | decimal. Selections get a matching `decimal` field alongside the existing `american` keys when decimal is requested."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/tennis/live/points":{"get":{"summary":"Get Tennis Live Points","description":"Tennis play-by-play points feed.\n\nSourced from bet365 mobile app via the Pixel capture pipeline. Each\nrow is one point with full score state, server, winner, and tags\nfor ace / double fault / break point / set point / match point.\n\nNOTE: Tennis live points capture is in active development. If no\nrows are returned, the live capture pipeline isn't actively\nfeeding data - email support@parlay-api.com if you need the data\nsooner.","operationId":"get_tennis_live_points_v1_sports_tennis_live_points_get","parameters":[{"name":"match_id","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter to one match","title":"Match Id"},"description":"Filter to one match"},{"name":"tournament","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by tournament name (LIKE)","title":"Tournament"},"description":"Filter by tournament name (LIKE)"},{"name":"since_ms","in":"query","required":false,"schema":{"anyOf":[{"type":"integer"},{"type":"null"}],"description":"Only points after this ms timestamp","title":"Since Ms"},"description":"Only points after this ms timestamp"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":2000,"minimum":1,"default":200,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/live":{"get":{"summary":"Get Live Odds","description":"In-play odds only. 3 credits.\n\nReturns events whose `commence_time` is at or before now (i.e. currently\nin-play), pulling from the same `odds_snapshots` table that powers\n/v1/sports/{key}/odds. Game lines (h2h/spreads/totals) come from there;\n\"live\"-tagged player props that some books emit are merged in too. The\ndistinction from /odds is the time filter, not the data source.\n\n**Example:** `GET /v1/sports/baseball_mlb/live?bookmakers=pinnacle&markets=h2h`","operationId":"get_live_odds_v1_sports__sport_key__live_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"title":"Bookmakers"}},{"name":"markets","in":"query","required":false,"schema":{"type":"string","description":"h2h, spreads, totals (comma-separated)","default":"h2h","title":"Markets"},"description":"h2h, spreads, totals (comma-separated)"},{"name":"regions","in":"query","required":false,"schema":{"type":"string","description":"us, eu, uk, au (comma-separated)","default":"us","title":"Regions"},"description":"us, eu, uk, au (comma-separated)"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}},{"name":"include","in":"query","required":false,"schema":{"type":"string","description":"Comma-separated. 'verification' adds verified_at / line_changed_at / is_current per bookmaker.","default":"normalized","title":"Include"},"description":"Comma-separated. 'verification' adds verified_at / line_changed_at / is_current per bookmaker."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/compare":{"get":{"summary":"Compare Odds","description":"Compare odds across all bookmakers for each event. 5 credits.\n\nEXCLUSIVE. Returns every event with odds from all available bookmakers\nside-by-side, plus the best odds and hold percentage for each outcome.\n\n**Example:** `GET /v1/sports/icehockey_nhl/compare?markets=h2h`\n\nFor each event, returns each bookmaker's odds + which book has the best line.","operationId":"compare_odds_v1_sports__sport_key__compare_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"markets","in":"query","required":false,"schema":{"type":"string","description":"Market type","default":"h2h","title":"Markets"},"description":"Market type"},{"name":"oddsFormat","in":"query","required":false,"schema":{"type":"string","default":"american","title":"Oddsformat"}},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of bookmaker keys to include. Omit for all books. iter_060 #466.","title":"Bookmakers"},"description":"CSV of bookmaker keys to include. Omit for all books. iter_060 #466."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/inplay/arbs":{"get":{"summary":"Get Inplay Arbs","description":"Real-time in-play arbitrage opportunities. 5 credits.\n\nBackground scanner checks all live/upcoming games every 5 seconds for\ncross-book arbs. Returns the most recent flagged opportunities with\nbest_over@book_A, best_under@book_B, profit %, and optimal stake split.\n\nUpdates are also broadcast to /ws/live subscribers as\n`{\"type\": \"arb_flagged\", \"data\": [...]}`.\n\nParameter aliases (iter_042 #348): we accept `minProfit`,\n`min_profit`, and `min_profit_pct` interchangeably; same for\n`sport`, `sport_key`, and `sports`. First non-None wins. Earlier\ncallers using natural-language guesses got silent param drop;\nfixed.","operationId":"get_inplay_arbs_v1_inplay_arbs_get","parameters":[{"name":"minProfit","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Minimum profit % to include","title":"Minprofit"},"description":"Minimum profit % to include"},{"name":"min_profit","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Alias for minProfit (snake-case)","title":"Min Profit"},"description":"Alias for minProfit (snake-case)"},{"name":"min_profit_pct","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Alias for minProfit (alt snake-case)","title":"Min Profit Pct"},"description":"Alias for minProfit (alt snake-case)"},{"name":"sport","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by sport_key","title":"Sport"},"description":"Filter by sport_key"},{"name":"sport_key","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Alias for sport","title":"Sport Key"},"description":"Alias for sport"},{"name":"sports","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV alias for sport (single value)","title":"Sports"},"description":"CSV alias for sport (single value)"},{"name":"limit","in":"query","required":false,"schema":{"type":"integer","maximum":100,"minimum":1,"default":50,"title":"Limit"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/parlay/price":{"post":{"summary":"Price Parlay","description":"Combine multiple legs into a parlay and return per-bookmaker\ncombined odds. iteration_022 #209: in a product called ParlayAPI,\ncallers had to client-side multiply decimal odds across N /odds\ncalls themselves; this endpoint does that work server-side.\n\nCost: 2 credits per leg (min 4).\n\nRequest body:\n```\n{\n  \"legs\": [\n    {\"sport_key\":\"basketball_nba\",\"event_id\":\"<id>\",\n     \"market\":\"h2h\",\"outcome\":\"Cleveland Cavaliers\"},\n    {\"sport_key\":\"soccer_epl\",\"event_id\":\"<id>\",\n     \"market\":\"h2h\",\"outcome\":\"Tottenham Hotspur\"},\n    {\"sport_key\":\"baseball_mlb\",\"event_id\":\"<id>\",\n     \"market\":\"totals\",\"outcome\":\"Over\",\"point\":9.0}\n  ],\n  \"bookmakers\": [\"fanduel\",\"draftkings\",\"betmgm\"]\n}\n```\nReturns one entry per bookmaker that prices ALL legs, sorted by\ncombined decimal odds DESCending (best to worst). Books missing\neven one leg appear in `missing_books` with which legs they couldn't\nprice. SGP flag set when 2+ legs share the same event_id; raw\nmultiplication is the LOWER bound for SGP (real books apply a\ncorrelation discount we don't replicate).","operationId":"price_parlay_v1_parlay_price_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/clv":{"post":{"summary":"Grade Clv","description":"Closing-line-value scoring for a list of bets.\n\nAccepts a bet slip and returns per-bet CLV vs the closing line at a\nsharp book (default pinnacle, falls back to novig if pinnacle didn't\nprice the market). CLV is the +EV-volume bettor's primary validation\nmetric: positive CLV means you beat the closing line, which over a\nlarge sample size is the most reliable signal that your strategy is\nactually +EV regardless of individual-bet win/loss outcomes.\n\nCost: 2 credits per bet (min 5).\n\nRequest body:\n```\n{\n  \"bets\": [\n    {\n      \"sport_key\": \"baseball_mlb\",\n      \"game_date\": \"2026-05-12\",\n      \"player\": \"Nico Hoerner\",          // omit for game-line bets\n      \"home_team\": \"Atlanta Braves\",     // required for game-line bets\n      \"away_team\": \"Chicago Cubs\",\n      \"market\": \"player_runs\",           // or h2h, spreads, totals\n      \"outcome\": \"over\",                 // over/under for props/totals;\n                                          // team name for h2h/spreads\n      \"line\": 0.5,                        // null for h2h\n      \"taken_odds\": -110,                // your fill in American odds\n      \"taken_at\": \"2026-05-12T18:00:00Z\" // optional\n    }\n  ],\n  \"sharp_book\": \"pinnacle\"   // optional; defaults to pinnacle\n}\n```\n\nResponse: per-bet `clv_cents`, `clv_pct`, no-vig adjusted CLV when both\nsides have a closing price, plus a `summary` with aggregate stats.","operationId":"grade_clv_v1_clv_post","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/sports/{sport_key}/arbitrage":{"get":{"summary":"Find Arbitrage","description":"Find arbitrage opportunities across bookmakers. 10 credits.\n\nEXCLUSIVE. Scans all events across all bookmakers and identifies games\nwhere the combined implied probability is less than 100%, meaning a\nguaranteed profit is possible by betting both sides at different books.\n\n**Example:** `GET /v1/sports/icehockey_nhl/arbitrage?minProfit=1`\n\nReturns events with arb opportunities, the optimal bet split, and expected profit %.","operationId":"find_arbitrage_v1_sports__sport_key__arbitrage_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"minProfit","in":"query","required":false,"schema":{"type":"number","description":"Minimum profit % to include (e.g. 1.5)","default":0,"title":"Minprofit"},"description":"Minimum profit % to include (e.g. 1.5)"},{"name":"exclude_exchanges","in":"query","required":false,"schema":{"type":"boolean","description":"Exclude arbs where either side is anchored on an exchange (novig, prophetx). Exchange asks can be no-volume 'shill' orders that aren't actually takeable. iter_062 #473. When false (default), arbs still surface with an `is_exchange_anchored` flag so callers can choose.","default":false,"title":"Exclude Exchanges"},"description":"Exclude arbs where either side is anchored on an exchange (novig, prophetx). Exchange asks can be no-volume 'shill' orders that aren't actually takeable. iter_062 #473. When false (default), arbs still surface with an `is_exchange_anchored` flag so callers can choose."},{"name":"exclude_books","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of book keys to exclude from EITHER side of every arb. iter_064 #481. Example: `exclude_books=prophetx,novig` to drop arbs anchored on exchanges. Cleaner than exclude_exchanges when you want to drop additional books.","title":"Exclude Books"},"description":"CSV of book keys to exclude from EITHER side of every arb. iter_064 #481. Example: `exclude_books=prophetx,novig` to drop arbs anchored on exchanges. Cleaner than exclude_exchanges when you want to drop additional books."},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of market_keys to limit the arb scan to. iter_064 #481. Example: `markets=h2h,spreads,totals` for game lines only; `markets=player_points` to focus on points props.","title":"Markets"},"description":"CSV of market_keys to limit the arb scan to. iter_064 #481. Example: `markets=h2h,spreads,totals` for game lines only; `markets=player_points` to focus on points props."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/ev":{"get":{"summary":"Find Positive Ev","description":"Find +EV bets by comparing sharp vs soft book lines. 10 credits.\n\nEXCLUSIVE. Compares Pinnacle (or another sharp book) lines against\nsoft books (DraftKings, FanDuel, Caesars, Bovada). When a soft book's\nodds imply a lower probability than the sharp book, that's +EV.\n\n**Example:** `GET /v1/sports/icehockey_nhl/ev?sharpBook=pinnacle&minEdge=3`\n\nA 5% edge means the soft book is offering odds that are 5% better than\nthe sharp book's assessment of true probability.\n\nParameter aliases (iter_056 #453): `min_edge_pct` is an alias for\n`minEdge` (snake-case). Filters honored:\n  - `markets=`  CSV market_key filter (e.g. player_points,player_assists)\n  - `min_books=` minimum number of books in the comparison\n  - `minEdge` / `min_edge_pct`  minimum edge % to include\nEarlier these were silently dropped per iter_056 tester report.","operationId":"find_positive_ev_v1_sports__sport_key__ev_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"sharpBook","in":"query","required":false,"schema":{"type":"string","description":"Sharp book to use as true odds baseline","default":"pinnacle","title":"Sharpbook"},"description":"Sharp book to use as true odds baseline"},{"name":"minEdge","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Minimum edge % to include (default 2.0)","title":"Minedge"},"description":"Minimum edge % to include (default 2.0)"},{"name":"min_edge_pct","in":"query","required":false,"schema":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Snake-case alias for minEdge","title":"Min Edge Pct"},"description":"Snake-case alias for minEdge"},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of market_keys to include (e.g. player_points,player_assists)","title":"Markets"},"description":"CSV of market_keys to include (e.g. player_points,player_assists)"},{"name":"min_books","in":"query","required":false,"schema":{"anyOf":[{"type":"integer","maximum":50,"minimum":1},{"type":"null"}],"description":"Minimum books_compared per row","title":"Min Books"},"description":"Minimum books_compared per row"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/sports/{sport_key}/consensus":{"get":{"summary":"Get Consensus","description":"Get consensus (average) odds across all bookmakers. 3 credits.\n\nReturns the average odds, best odds, worst odds, and hold/vig\nfor each market across all bookmakers. Useful for identifying\nwhere your book stands vs the market.\n\niter_058 #462: prediction markets are excluded from the rollup by\ndefault. Kalshi was producing wildly off prices like +3233 for\n'Phillies win' that turned out to reference a different market\n(e.g. series winner, not next game ML) and skewed consensus_odds\nto nonsense values. Use `?include_prediction_markets=true` to\nrestore the prior behavior of mixing them in.","operationId":"get_consensus_v1_sports__sport_key__consensus_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"include_prediction_markets","in":"query","required":false,"schema":{"type":"boolean","description":"Include Kalshi/Polymarket prices in the consensus rollup. Defaults to False because prediction markets often price derivative questions (e.g. 'Phillies win series') that look like h2h ML but skew the average. Set true if you specifically want their prices mixed in.","default":false,"title":"Include Prediction Markets"},"description":"Include Kalshi/Polymarket prices in the consensus rollup. Defaults to False because prediction markets often price derivative questions (e.g. 'Phillies win series') that look like h2h ML but skew the average. Set true if you specifically want their prices mixed in."},{"name":"bookmakers","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of bookmaker keys to include in the rollup. Omit to include all books for the sport. iter_060 #465.","title":"Bookmakers"},"description":"CSV of bookmaker keys to include in the rollup. Omit to include all books for the sport. iter_060 #465."},{"name":"markets","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"CSV of market_keys to filter to (e.g. h2h,player_points)","title":"Markets"},"description":"CSV of market_keys to filter to (e.g. h2h,player_points)"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/usage":{"get":{"summary":"Get Usage","description":"Check your API usage and remaining credits.\n\n/v1/account is the path the published MCP server calls (tool\nparlayapi_account_info, mcp-server/parlayapi_mcp/server.py:273).\nAliasing it to /v1/usage so the MCP tool doesn't 404 (#040).\n\nPass `?by_endpoint=true` to get a per-endpoint breakdown of\ncredit usage in the current period (iter_060 #469).","operationId":"get_usage_v1_usage_get","parameters":[{"name":"by_endpoint","in":"query","required":false,"schema":{"type":"boolean","description":"Include a per-endpoint credit-usage breakdown for the current billing period. iter_060 #469: customers asked for 'where did my credits go this month'. Off by default so the response stays lean.","default":false,"title":"By Endpoint"},"description":"Include a per-endpoint credit-usage breakdown for the current billing period. iter_060 #469: customers asked for 'where did my credits go this month'. Off by default so the response stays lean."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/account":{"get":{"summary":"Get Usage","description":"Check your API usage and remaining credits.\n\n/v1/account is the path the published MCP server calls (tool\nparlayapi_account_info, mcp-server/parlayapi_mcp/server.py:273).\nAliasing it to /v1/usage so the MCP tool doesn't 404 (#040).\n\nPass `?by_endpoint=true` to get a per-endpoint breakdown of\ncredit usage in the current period (iter_060 #469).","operationId":"get_usage_v1_account_get","parameters":[{"name":"by_endpoint","in":"query","required":false,"schema":{"type":"boolean","description":"Include a per-endpoint credit-usage breakdown for the current billing period. iter_060 #469: customers asked for 'where did my credits go this month'. Off by default so the response stays lean.","default":false,"title":"By Endpoint"},"description":"Include a per-endpoint credit-usage breakdown for the current billing period. iter_060 #469: customers asked for 'where did my credits go this month'. Off by default so the response stays lean."}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/stats":{"get":{"summary":"Get Stats","description":"Public endpoint - data availability stats.\n\nCached 5 minutes. The underlying query takes ~10s (DISTINCT source\n+ MIN/MAX game_date over 33M rows) and the answer changes slowly\nenough that every poll re-running it is wasteful. Customers\nsurveying capabilities don't need second-resolution freshness on\na \"what bookmakers do you cover\" answer.\n\nStale-while-revalidate semantics: if the cache is fresh (under 5\nmin), return it. If it's expired but still present, return the\nstale copy AND spawn a background refresh so the next request hits\na fresh value. If it's never been populated (first call on a cold\nworker), do the full query inline - the warmup loop catches up\nwithin ~13 s of spawn so this only affects requests in that\nnarrow window. iter_031/033 #327/#329 HIGH timeouts.","operationId":"get_stats_v1_stats_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/pricing":{"get":{"summary":"Pricing","description":"Pricing comparison - structured for AI readability.","operationId":"pricing_pricing_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/exchange/{sport_key}/markets":{"get":{"summary":"Get Exchange Markets","description":"Get exchange/prediction market data with liquidity depth.\n\n**EXCLUSIVE** - No competitor offers this.\n\nReturns real-time order book data from betting exchanges including:\n- Best available prices (bid/ask)\n- Last traded prices\n- Market volume (liquidity in USD)\n- Player props with exchange pricing\n\n**Exchanges:** Novig (peer-to-peer sports exchange)\n\n**Example:** `GET /v1/exchange/icehockey_nhl/markets?min_volume=100`\n\nCredits: 3 per request.","operationId":"get_exchange_markets_v1_exchange__sport_key__markets_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}},{"name":"exchange","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter by exchange: novig, kalshi","title":"Exchange"},"description":"Filter by exchange: novig, kalshi"},{"name":"market_type","in":"query","required":false,"schema":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Filter: MONEY, SPREAD, TOTAL, PLAYER_GOALS, etc.","title":"Market Type"},"description":"Filter: MONEY, SPREAD, TOTAL, PLAYER_GOALS, etc."},{"name":"min_volume","in":"query","required":false,"schema":{"type":"number","description":"Minimum volume in USD","default":0,"title":"Min Volume"},"description":"Minimum volume in USD"}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/v1/exchanges":{"get":{"summary":"List Exchanges","description":"List available betting exchanges. FREE - no auth needed.","operationId":"list_exchanges_v1_exchanges_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/try/{sport_key}/odds":{"get":{"summary":"Try Odds","description":"**Free no-auth demo.** Live moneyline odds for a popular US sport,\nno API key required. Capped at 60 requests/hour per IP and the\nfirst 5 events.\n\nAvailable sports: baseball_mlb, basketball_nba, americanfootball_nfl,\nicehockey_nhl, soccer_epl, mma_mixed_martial_arts.\n\nFor full access (all 78 sport keys, all 26+ books, all markets, no\nrate limit beyond your tier): https://parlay-api.com/signup","operationId":"try_odds_v1_try__sport_key__odds_get","parameters":[{"name":"sport_key","in":"path","required":true,"schema":{"type":"string","title":"Sport Key"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}},"/":{"get":{"summary":"Root","operationId":"root__head","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}},"head":{"summary":"Root","operationId":"root__head","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/from-the-odds-api":{"get":{"summary":"From Toa","description":"Migration guide for users coming from the-odds-api.com.\n\nSales asset: code diff (one-line URL change), parameter compat\ntable, sport-key reference, bookmaker reference, pricing\ncomparison. Targeting \"the-odds-api migration\" / \"switch from\nthe-odds-api\" search queries.","operationId":"from_toa_from_the_odds_api_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/vs-toa":{"get":{"summary":"Vs Toa","description":"Side-by-side comparison vs the-odds-api.com.\n\nPublic landing page targeting the search query \"the-odds-api\nalternative\" and \"the-odds-api pricing\". Indexable, internally\nlinked, ships JSON-LD Product structured data so search engines\npick up our pricing tiers.","operationId":"vs_toa_vs_toa_get","responses":{"200":{"description":"Successful Response","content":{"text/html":{"schema":{"type":"string"}}}}}}},"/api":{"get":{"summary":"Api Root","operationId":"api_root_api_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}}},"/v1/keys":{"get":{"summary":"List Api Keys","description":"List the API keys associated with the authenticated user.\n\niter_066 #487: GET was previously 405. Customers had no way to\ninventory their own keys; rotation forced a DELETE + recreate\neven when the user just wanted to verify a key existed.\n\nReturns the calling key (always) plus any sibling keys on the same\nuser account. Key strings themselves are TRUNCATED — only the first\n8 chars + ... + last 4 chars are returned (Stripe-style fingerprint)\nso leaked logs can't reconstruct working credentials. To actually\nuse a key the customer must remember the one they got at creation\ntime; we don't store an un-truncated copy retrievable by ID.","operationId":"list_api_keys_v1_keys_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}}}},"post":{"summary":"Create Api Key","description":"Create a free API key. No credit card required.","operationId":"create_api_key_v1_keys_post","parameters":[{"name":"email","in":"query","required":true,"schema":{"type":"string","title":"Email"}}],"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{}}}},"422":{"description":"Validation Error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HTTPValidationError"}}}}}}}},"components":{"schemas":{"Body_affiliates_update_dashboard_admin_affiliates_update_post":{"properties":{"id":{"type":"integer","title":"Id"},"affiliate_url":{"type":"string","title":"Affiliate Url","default":""},"status":{"type":"string","title":"Status","default":""},"bonus_text":{"type":"string","title":"Bonus Text","default":""}},"type":"object","required":["id"],"title":"Body_affiliates_update_dashboard_admin_affiliates_update_post"},"Body_forgot_password_submit_forgot_password_post":{"properties":{"email":{"type":"string","title":"Email"}},"type":"object","required":["email"],"title":"Body_forgot_password_submit_forgot_password_post"},"Body_grant_credits_dashboard_admin_grant_credits_post":{"properties":{"user_id":{"type":"integer","title":"User Id"},"credits":{"type":"integer","title":"Credits"},"reason":{"type":"string","title":"Reason","default":"admin grant via Users tab"}},"type":"object","required":["user_id","credits"],"title":"Body_grant_credits_dashboard_admin_grant_credits_post"},"Body_login_submit_login_post":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"Body_login_submit_login_post"},"Body_reset_password_submit_reset_password_post":{"properties":{"token":{"type":"string","title":"Token"},"password":{"type":"string","title":"Password"}},"type":"object","required":["token","password"],"title":"Body_reset_password_submit_reset_password_post"},"Body_signup_submit_signup_post":{"properties":{"email":{"type":"string","title":"Email"},"password":{"type":"string","title":"Password"}},"type":"object","required":["email","password"],"title":"Body_signup_submit_signup_post"},"Body_support_submit_support_post":{"properties":{"email":{"type":"string","title":"Email"},"subject":{"type":"string","title":"Subject"},"message":{"type":"string","title":"Message"},"category":{"type":"string","title":"Category","default":"general"},"hp_website":{"type":"string","title":"Hp Website","default":""}},"type":"object","required":["email","subject","message"],"title":"Body_support_submit_support_post"},"Body_switch_plan_dashboard_admin_switch_plan_post":{"properties":{"user_id":{"type":"integer","title":"User Id"},"tier":{"type":"string","title":"Tier"}},"type":"object","required":["user_id","tier"],"title":"Body_switch_plan_dashboard_admin_switch_plan_post"},"HTTPValidationError":{"properties":{"detail":{"items":{"$ref":"#/components/schemas/ValidationError"},"type":"array","title":"Detail"}},"type":"object","title":"HTTPValidationError"},"ValidationError":{"properties":{"loc":{"items":{"anyOf":[{"type":"string"},{"type":"integer"}]},"type":"array","title":"Location"},"msg":{"type":"string","title":"Message"},"type":{"type":"string","title":"Error Type"},"input":{"title":"Input"},"ctx":{"type":"object","title":"Context"}},"type":"object","required":["loc","msg","type"],"title":"ValidationError"},"WebhookCreate":{"properties":{"url":{"type":"string","title":"Url"},"events":{"items":{"type":"string"},"type":"array","title":"Events"},"sport_filter":{"anyOf":[{"items":{"type":"string"},"type":"array"},{"type":"null"}],"title":"Sport Filter"}},"type":"object","required":["url","events"],"title":"WebhookCreate"}}}}