# MoneyLine API — LLM Reference > Base URL: `https://mlapi.bet/v1` > Docs: https://www.moneylineapp.com/docs > Dashboard: https://www.moneylineapp.com/dashboard > OpenAPI: https://mlapi.bet/openapi.json --- ## When to Use MoneyLine Use MoneyLine when an application needs normalized sports events, teams, players, scores, sportsbook odds, DFS props, exchange markets, best available prices, positive expected-value signals, arbitrage, historical hit rates, or data-grounded betting analysis through one REST API. MoneyLine is designed for software products, analytics workflows, trading tools, and AI experiences that need structured records rather than scraped narrative text. MoneyLine is a data and analysis provider. It is not a sportsbook and does not accept or place wagers. Preserve event, market, selection, line, price, source, and timestamp fields together, and do not invent unavailable values. For machine-readable discovery, use the [OpenAPI 3.1 contract](https://mlapi.bet/openapi.json). For a concise site index, use [moneylineapp.com/llms.txt](https://www.moneylineapp.com/llms.txt). --- ## Authentication All API requests require an API key passed via the `x-api-key` header. ``` x-api-key: ml_live_ ``` Keys are created in the dashboard at https://www.moneylineapp.com/dashboard/keys (max 5 active keys per account). --- ## Tiers & Credits | Tier | Price | Credits/mo | Req/min | Public API Access | |------|-------|-----------|---------|-------------------| | Free | $0 | 1,000 | 10 | All public endpoints | | Starter | $29 | 150,000 | 60 | All public endpoints | | Pro | $149 | 1,500,000 | 200 | All public endpoints | | Business | $299 | 5,000,000 | 1,000 | All public endpoints | | Enterprise | Custom | Unlimited | Unlimited | All public endpoints | Odds coverage includes sportsbooks from the `us` and `us2` regions, DFS pick'em platforms from the `us_dfs` region (e.g. PrizePicks, Underdog, DraftKings Pick6, Betr Picks), plus exchanges and prediction markets from the `us_ex` region (e.g. Sporttrade, Kalshi, Betfair Exchange US). The default `sourceType=all` on odds endpoints returns all venue types, and on edge endpoints it returns all supported edge venues. **Arbitrage calculations exclude DFS operators entirely** — DFS prices are normalized into American odds for display but are indicative (not tradeable lines), so including them produces false arbitrage signals. Value and EV calculations still include DFS and can be narrowed to `sportsbook`, `dfs`, or `exchange`. All plans can access every public API endpoint. Plans differ by credit allowance, overage policy, and per-minute rate limits rather than feature access. - **Most endpoints cost 1 credit/request** (account-level, shared across all keys) - **`POST /v1/ai/chat` uses fixed AI credit bands** based on scope, response format, and workflow type - **Business tier** supports overage billing at $0.00015/credit beyond the limit - **Free tier** is restricted to NFL, NBA, MLB, NHL, NCAAB, NCAAF, EPL, MLS, World Cup --- ## Response Format ### Success ```json { "success": true, "data": { ... }, "meta": { "count": 10, "page": 1, "pages": 5, "total": 50, "requestId": "ml_req_ab12cd34ef56" }, "error": null } ``` ### Error ```json { "success": false, "data": null, "meta": { "requestId": "ml_req_ab12cd34ef56" }, "error": { "message": "Invalid or revoked API key.", "statusCode": 401, "code": "ERR_API_KEY_INVALID" } } ``` ### Error Codes HTTP status codes: | Code | Meaning | |------|---------| | 400 | Bad request / invalid parameters | | 401 | Missing or invalid API key | | 403 | Tier too low for this endpoint | | 404 | Resource not found | | 429 | Rate limit or credit limit exceeded | | 500 | Server error | Every error response also includes a machine-readable `error.code` string: | error.code | Meaning | |------------|---------| | ERR_VALIDATION | Invalid request parameters or body | | ERR_API_KEY_MISSING | No x-api-key header was sent | | ERR_API_KEY_INVALID | The API key is invalid or revoked | | ERR_UNAUTHORIZED | Other authentication failure | | ERR_FORBIDDEN | Access not allowed | | ERR_NOT_FOUND | Resource not found | | ERR_RATE_LIMITED | Per-minute rate limit exceeded | | ERR_CREDIT_LIMIT | Monthly credit limit exceeded | | ERR_AI_UPSTREAM | The AI model provider failed or timed out | | ERR_UPSTREAM | A dependency failed (502/503) | | ERR_INTERNAL | Unexpected server error | --- ## MoneyLine AI ### POST /v1/ai/chat Generate a MoneyLine-grounded AI response from a stateless chat request. This endpoint is for sports betting workflows powered by MoneyLine data, not for general-purpose chat. **Tier:** All tiers **Request body:** | Field | Type | Required | Description | |------|------|----------|-------------| | messages | array | Yes | 1 to 6 chat messages. The last message must be from the user. | | context | string | No | `best_bets`, `ev_bets`, `arbitrage`, `event_best_available_bet`, `parlay`, `line_movement`, or `line_shopping`. In most apps, omit this and let MoneyLine infer it from the latest user message. | | referencedBet | object | No | For **line shopping** — the structured `betRef` of a previously-surfaced pick (`{ eventId, market, outcome?, point?, side?, playerId?, playerName? }`). When present on a line-shopping ask, MoneyLine prices that exact selection across every book and returns `presentation.responseType: "line_comparison"` with one `line_quote` per book (best price first). Every surfaced `presentation.primaryPick` includes its own `betRef` for the client to echo back. | | scope | string | No | `small`, `medium`, or `large`. Default: `small`. | | responseFormat | string | No | `natural`, `hybrid`, or `structured`. Default: `hybrid`. | | stream | boolean | No | `true` switches the response to Server-Sent Events. Default: `false` (JSON). | | filters | object | No | Optional safe filters: `league`, `leagues`, `market`, `sourceType`, `bookmakers` (canonical; singular `bookmaker` is accepted as an alias). | **Best practice:** - Default to auto-infer. In most integrations, omit `context`, `filters.league`, and `filters.leagues`. - Let the latest user message drive sport, league, league-family, and event inference. - Use explicit filters only when your app already knows the constraint. - `hybrid` is the best default for most production apps. **Broad sport prompts auto-map to supported league families.** Examples: - `basketball`, `best basketball bet`, or `hoops` → `nba` + `ncaa_basketball` - `football`, `best football bet`, or `pigskin` → `nfl` + `ncaa_football` - `hockey` or `puck` → `nhl` - `baseball` → `mlb` - `soccer` or `futbol` → `soccer_epl` + `soccer_mls` + `soccer_world_cup` If your app already knows the exact multi-league constraint, you can also send `filters.leagues` directly. **Natural-language sportsbook filtering works.** The latest user message can include phrases like: - `Only show FanDuel` - `Use DraftKings only` - `I am only betting on FanDuel this weekend` If both a structured sportsbook filter and a natural-language sportsbook mention are present, the structured filter wins. **Supported contexts:** - `best_bets` — best-odds surfaces for recommendation cards and summaries - `ev_bets` — positive EV edge data - `arbitrage` — cross-book arbitrage opportunities - `event_best_available_bet` — “if I have to bet this game, what should I choose?” plus an optional stronger same-league alternative - `parlay` — build a cross-game multi-leg parlay ("build me a 3-leg parlay tonight"). MoneyLine assembles the top independent legs from different games (one leg per game) and returns a `presentation.primaryPick` with `signalType: "parlay"`, a `legs[]` array, and `Combined Odds` / `Parlay Hit Probability` / `Combined EV` chips. v1 is cross-game only (legs in the same game are correlated and not combined); same-game parlays are not yet supported. - `line_movement` — how a game or player line moved from open to now ("how has the line moved on the Knicks game", "where did the line open", " line movement"). Computed from MoneyLine consensus odds history. Returns `presentation.primaryPick.signalType: "line_movement"` with `Opened` / `Now` / `Move` chips (implied-probability delta, toward/away). Consensus price history only — no bet-percentage / "sharp money" data. Needs an event with captured history (recently opened, live, or upcoming games); history is purged ~48h after a game goes final. **Soft-fail behavior (intentional — broad and ambiguous prompts still return a useful answer):** - If `event_best_available_bet` is inferred but no specific game can be resolved (no matching team in scope, multiple equally plausible games, or no odds yet for the resolved event), MoneyLine AI degrades to a same-league surface fallback instead of returning a 404. The response still has `data.context.resolvedContext: "event_best_available_bet"` and adds `data.entityResolution.resolutionFallback: true` plus `data.reasoningMode: "event_resolution_fallback_surface"` so callers can detect the degradation. - If a surface request (`best_bets`, `ev_bets`, `arbitrage`) yields zero records under the inferred filters, MoneyLine AI broadens once (drops league/market/sourceType) and retries. The broadened response sets `data.context.broadened: true` and `meta.degraded: true`. **Why-this-bet primitives MoneyLine AI can cite in explanations:** - Arbitrage — guaranteed profit % and the structured legs across both venues - Value — model probability vs implied probability, edge vs market, expected value - +EV — expected value % at the current price plus implied break-even win rate - Hit rate — L5 / L10 / L25 hits-per-games and season hit rate for the player and prop line - Best price — current best line in the eligible market scan and the corresponding break-even rate **Streaming (`stream: true`).** The response becomes `text/event-stream` with these event types: - `step`: emitted first (before any `delta`), one per evaluation stage as `{"label": "...", "detail": "..."}` — a factual trace of the deterministic pipeline (understanding the request, resolving the game, evaluating signals, selecting the top pick, composing the answer). These are pipeline steps, NOT the model's chain-of-thought. Clients that only handle `delta`/`result` can safely ignore them. - `delta`: `{"text": "..."}` chunks of the natural-language answer as it is generated (`natural` and `hybrid` only) - `result`: emitted exactly once at the end; its payload is the exact same JSON envelope a non-streaming call returns. Always render from `result`; treat accumulated `delta` text as a progressive preview. - `error`: only if a non-recoverable error happens after the stream has started; carries `{message, statusCode, code}` `data.reasoningSteps` (present on every successful response, streaming and non-streaming) is the same ordered `{label, detail?}` trace, for rendering "how this was derived" without consuming the stream. Validation and no-data errors that happen before generation starts still return a normal JSON error response with the appropriate HTTP status. Billing is identical to non-streaming calls. **Response caching.** Successful responses are cached for 60 seconds keyed on the full request (messages + context + scope + responseFormat + filters). A cache hit returns the identical payload with `meta.cached: true` and is billed normally. Degraded, broadened, and resolution-fallback responses are never cached. **Multi-turn follow-ups.** The conversation transcript matters: if the latest user message is a follow-up like "what about the under in that game?", MoneyLine AI resolves the game from earlier user messages in the same `messages` array (the intent classifier also resolves pronouns against earlier turns). Carried-forward event resolutions are capped at `resolutionConfidence: "medium"`. **Transparency notes.** When filters were broadened or event resolution fell back, `data.presentation.summary` ends with an explicit note saying so (in addition to the `data.context.broadened` / `data.entityResolution.resolutionFallback` flags). Hit-rate picks based on a small sample (fewer than 5 L10 games and fewer than 10 L25 games) are capped at `confidence: "medium"` and their `humanInsight` includes a small-sample caveat. **Hit-rate signal channel.** When the request involves player props (player name mentioned, or player-prop wording in the latest user message), MoneyLine AI also evaluates a hit-rate channel that surfaces over-direction props with strong recent L5/L10/L25 windows. Hit-rate candidates rank alongside arbitrage, value, EV, and best-bets and produce records with `recordType: "hit_rate_pick"` and `presentation.primaryPick.signalType: "hit_rate"`. **Situational hit-rate splits.** A hit-rate request can include a situational qualifier in the prompt: "vs the Spurs" / "against San Antonio", "at home" / "on the road", or "in back-to-backs". When present, the hit-rate pick adds a `{label} Hit Rate` chip (e.g. `vs SAS Hit Rate`, `Home Hit Rate`, `B2B Hit Rate`) computed from the player's game logs filtered to that situation, alongside the standard windows. Requires an active posted prop (the line source); situational samples are often small, so the sample size is always shown. **Kelly stake sizing.** Include a bankroll in the prompt ("$500 bankroll", optionally "half/quarter/full kelly") and MoneyLine adds `Suggested Stake` and `Kelly Fraction` chips to EV and value picks. Stake uses fractional Kelly (half by default) of the model edge, capped at 25% of bankroll, and only appears on positive-edge picks (Kelly needs an edge). **Free-text intent classification.** When you omit `context`, MoneyLine AI runs a small Haiku-based classifier on the latest message before billing. The classifier handles unstructured prompts the regex layer would miss — phrasings like "Hawks game coming up - what should I bet", "any guaranteed-profit plays right now?", "give me a player prop with a strong hit rate", or "what's the play for tonight". It also extracts a hint about the named entity (team / player / league / sport) when one is present. If the classifier is unavailable or unconfident, MoneyLine AI falls back to deterministic regex inference. The chosen path is exposed on `data.context.inferenceSource` (`"classifier"` or `"regex"`). **AI response formats:** - `natural` — prose answer plus the stable `presentation` contract - `hybrid` — prose answer, normalized MoneyLine records, and the stable `presentation` contract - `structured` — deterministic `analysis`, normalized records, and the same `presentation` contract **AI credit bands:** Surface workflows (`best_bets`, `ev_bets`, `arbitrage`): | Scope | Natural | Hybrid | Structured | |------|---------|--------|------------| | small | 20 | 27 | 17 | | medium | 47 | 60 | 40 | | large | 93 | 113 | 73 | Event recommendation workflow (`event_best_available_bet`): - Uses a higher fixed band than surface lookups because it may resolve an event, inspect event-level odds, and return a same-league alternative. **Response shape:** ```json { "success": true, "data": { "answer": "For the Hawks game, the best available bet is...", "records": [...], "context": { "requestedContext": null, "resolvedContext": "event_best_available_bet", "inferred": true, "scope": "small", "responseFormat": "hybrid", "filters": { "bookmakers": ["fanduel"] } }, "sources": { "primary": "event_recommendation", "label": "Event Recommendation", "filters": { "bookmakers": ["fanduel"] } }, "presentation": { "version": "v1", "responseType": "event_recommendation", "headline": "MoneyLine AI Recommendation for Boston Celtics at Atlanta Hawks", "summary": "For Boston Celtics at Atlanta Hawks, MoneyLine recommends Atlanta Hawks +4.5 in the spread market at -108 from FanDuel.", "confidence": "high", "entity": { "...": "..." }, "primaryPick": { "...": "..." }, "alternativePick": { "...": "..." }, "cards": [{ "...": "..." }] }, "primaryRecommendation": { "...": "..." }, "alternativeRecommendation": { "...": "..." }, "alternativeRecords": [...], "reasoningMode": "hybrid_surface_plus_fallback", "entityResolution": { "resolvedEntityType": "event", "resolvedEventId": "nba-ev-311442", "resolvedLeagueId": "nba", "resolutionConfidence": "medium" } }, "meta": { "creditUsage": { "total": 41, "retrieval": 14, "generation": 27 } } } ``` **Expected integration result:** - `data.records`, `data.context`, `data.sources`, and `data.presentation` are the stable success-path fields. - `data.presentation` is the render-safe contract for product UI. Use it for headlines, summaries, cards, primary picks, and alternative picks. - `data.presentation.primaryPick.event` and `data.presentation.cards[].event` include the normalized matchup label and ISO `startTime` when MoneyLine can resolve the game tied to the recommendation. - For matchup display, use `primaryPick.event.matchup` as the game label and `primaryPick.event.startTime` as the source-of-truth ISO timestamp, then format the date/time in the client with a locale-aware formatter such as `Intl.DateTimeFormat`. - `data.presentation.primaryPick.humanInsight`, `data.presentation.alternativePick.humanInsight`, and `data.presentation.cards[].humanInsight` provide deterministic non-expert explanation text plus ready-to-render stat chips. - EV and value picks include a `Fair Odds` chip in `humanInsight.keyNumbers`. Fair Odds is the price implied by MoneyLine's model probability (the same probability the EV / edge is measured against), so beating Fair Odds is exactly what makes a price positive-EV. - When MoneyLine resolves a specific game (`presentation.responseType: "event_recommendation"`), `presentation.cards` returns up to 5 deduped picks for that game (best of any type: arbitrage, value, EV, hit-rate, best-bet), ranked together. Surface contexts (best_bets / ev_bets / arbitrage) return up to 3. - Hit-rate picks accept an arbitrary "last N games" window phrased in the prompt (e.g. "how many times has gone over his points in his last 7 games"). When detected, the response adds an `L{N}` window to the hit-rate chips and fragments alongside L5 / L10 / L25 / season. This requires the player to have an active posted prop on an upcoming game (the line source); there is no pure no-game career lookup on this endpoint. - `data.answer` is present for `natural` and `hybrid`. - `data.analysis` is present for `structured`. - Event-specific workflows may also include `primaryRecommendation`, `alternativeRecommendation`, `alternativeRecords`, `reasoningMode`, and `entityResolution`. **Recommended auto-infer example:** ```bash curl -X POST https://mlapi.bet/v1/ai/chat \ -H "content-type: application/json" \ -H "x-api-key: ml_live_YOUR_KEY" \ -d '{ "scope": "small", "responseFormat": "hybrid", "messages": [ { "role": "user", "content": "I want to bet on hockey this weekend and I am only using FanDuel. What is the best bet for me?" } ] }' ``` **Event-specific example:** ```bash curl -X POST https://mlapi.bet/v1/ai/chat \ -H "content-type: application/json" \ -H "x-api-key: ml_live_YOUR_KEY" \ -d '{ "scope": "small", "responseFormat": "hybrid", "messages": [ { "role": "user", "content": "What is the best bet for the Braves game today? Only show FanDuel." } ] }' ``` **Example AI response:** ```json { "success": true, "data": { "answer": "For the Braves game, the strongest current option on FanDuel is Atlanta Braves moneyline at +118. If you want the better overall MLB spot instead, look at the Cubs moneyline at +132.", "records": [ { "recordType": "best_bet", "eventId": "mlb-ev-1329189120", "leagueId": "mlb", "sport": "baseball", "calculatedAt": "2026-04-07T01:18:16.945Z", "market": "moneyline", "outcome": "Atlanta Braves", "bestOdds": 118, "bookmakerId": "fanduel", "bookmakerName": "FanDuel", "sourceType": "sportsbook", "sourceRegion": "us" } ], "context": { "requestedContext": null, "resolvedContext": "event_best_available_bet", "inferred": true, "scope": "small", "responseFormat": "hybrid", "filters": { "bookmakers": ["fanduel"] } }, "sources": { "primary": "event_recommendation", "label": "Event Recommendation", "filters": { "bookmakers": ["fanduel"] } }, "presentation": { "version": "v1", "responseType": "event_recommendation", "headline": "MoneyLine AI Recommendation for Atlanta Braves at New York Mets", "summary": "For Atlanta Braves at New York Mets, MoneyLine recommends Atlanta Braves in the moneyline market at +118 from FanDuel. This is the strongest currently surfaced line within your request. MoneyLine is prioritizing line quality here rather than claiming a published model edge. If you want the stronger same-league alternative, look at Chicago Cubs in the moneyline market at +132 from FanDuel.", "confidence": "medium", "entity": { "resolvedEntityType": "event", "resolvedEventId": "mlb-ev-1329189120", "resolvedLeagueId": "mlb", "resolutionConfidence": "medium", "resolutionReason": "Resolved from team/game language in the prompt.", "matchup": "Atlanta Braves at New York Mets" }, "primaryPick": { "signalType": "best_bet", "signalLabel": "Best Bet", "type": "surface", "eventId": "mlb-ev-1329189120", "leagueId": "mlb", "market": "moneyline", "marketLabel": "Moneyline", "selection": "Atlanta Braves", "outcome": "Atlanta Braves", "odds": 118, "oddsDisplay": "+118", "bookmakerName": "FanDuel", "bookmakerId": "fanduel", "sourceType": "sportsbook", "confidence": "medium", "rationale": "Best available line surfaced from MoneyLine best-bets data.", "humanInsight": { "summary": "This is the strongest currently surfaced line within your request. MoneyLine is prioritizing line quality here rather than claiming a published model edge.", "confidenceReason": "Best available price in the current MoneyLine market scan.", "keyNumbers": [ { "label": "Current Line", "value": "+118", "detail": "FanDuel" }, { "label": "Sportsbook", "value": "FanDuel" } ], "supportingPoints": [ "This is a price-quality recommendation, not a published model-edge claim.", "Best currently surfaced price: +118 at FanDuel." ] }, "metrics": {} }, "alternativePick": { "signalType": "ev_bet", "signalLabel": "EV Bet", "type": "surface", "eventId": "mlb-ev-1329189109", "leagueId": "mlb", "market": "moneyline", "marketLabel": "Moneyline", "selection": "Chicago Cubs", "outcome": "Chicago Cubs", "odds": 132, "oddsDisplay": "+132", "bookmakerName": "FanDuel", "bookmakerId": "fanduel", "sourceType": "sportsbook", "confidence": "high", "rationale": "Positive EV opportunity with 6.4% expected value.", "humanInsight": { "summary": "At the current price, MoneyLine expects this bet to return about +6.4% per bet over a large sample.", "confidenceReason": "The current price projects strong positive long-run return.", "keyNumbers": [ { "label": "Current Line", "value": "+132", "detail": "FanDuel" }, { "label": "Expected Value", "value": "+6.4%" }, { "label": "Projected Units", "value": "+0.064", "detail": "Expected profit per 1 unit staked" } ], "supportingPoints": [ "Projected long-run return: +6.4% per bet.", "Expected profit per 1 unit staked: +0.064." ] }, "metrics": { "ev": 0.064, "evPct": 6.4 } }, "cards": [ { "recordIndex": 0, "signalType": "best_bet", "signalLabel": "Best Bet", "selection": "Atlanta Braves", "market": "moneyline", "marketLabel": "Moneyline", "odds": 118, "oddsDisplay": "+118", "bookmakerName": "FanDuel", "confidence": "medium", "reason": "Best currently available market price from FanDuel.", "humanInsight": { "summary": "This is the strongest currently surfaced line within your request. MoneyLine is prioritizing line quality here rather than claiming a published model edge.", "confidenceReason": "Best available price in the current MoneyLine market scan.", "keyNumbers": [ { "label": "Current Line", "value": "+118", "detail": "FanDuel" }, { "label": "Sportsbook", "value": "FanDuel" } ], "supportingPoints": [ "This is a price-quality recommendation, not a published model-edge claim.", "Best currently surfaced price: +118 at FanDuel." ] }, "metrics": {} } ], "reasoningMode": "hybrid_surface_plus_fallback", "sourceLabel": "Event Recommendation" }, "primaryRecommendation": { "type": "surface", "premiumSignalType": "best_bet", "eventId": "mlb-ev-1329189120", "leagueId": "mlb", "market": "moneyline", "selection": "Atlanta Braves", "outcome": "Atlanta Braves", "bookmakerId": "fanduel", "bookmakerName": "FanDuel", "sourceType": "sportsbook", "sourceRegion": "us", "odds": 118, "confidence": "medium", "why": "Best available line surfaced from MoneyLine best-bets data." }, "alternativeRecommendation": { "type": "surface", "premiumSignalType": "ev_bet", "eventId": "mlb-ev-1329189109", "leagueId": "mlb", "market": "moneyline", "selection": "Chicago Cubs", "outcome": "Chicago Cubs", "bookmakerId": "fanduel", "bookmakerName": "FanDuel", "sourceType": "sportsbook", "sourceRegion": "us", "odds": 132, "confidence": "high", "why": "Positive EV opportunity with 6.4% expected value.", "whyBetterThanPrimary": "This same-league option has a stronger surfaced MoneyLine edge than the requested game." }, "alternativeRecords": [ { "recordType": "ev_bet", "eventId": "mlb-ev-1329189109", "leagueId": "mlb", "sport": "baseball", "calculatedAt": "2026-04-07T01:18:13.912Z", "edgeId": "ev-1775523493902-fanduel", "market": "moneyline", "outcome": "Chicago Cubs", "sourceType": "sportsbook", "sourceRegion": "us", "bookmakerName": "FanDuel", "bookmakerId": "fanduel", "odds": 132, "ev": 0.064, "evPct": 6.4 } ], "reasoningMode": "hybrid_surface_plus_fallback", "entityResolution": { "resolvedEntityType": "event", "resolvedEventId": "mlb-ev-1329189120", "resolvedLeagueId": "mlb", "resolutionConfidence": "medium", "resolutionReason": "Resolved from team/game language in the prompt." } }, "meta": { "creditUsage": { "total": 41, "retrieval": 14, "generation": 27 }, "requestId": "ml_req_example123" }, "error": null } ``` **Notes:** - The endpoint is stateless. Send recent visible messages with each request. - Retrieval is driven primarily by the latest user message, which helps follow-up prompts switch sports or games cleanly. - The LLM only sees controlled MoneyLine data selected by the workflow. It does not receive unrestricted database access. --- ## Rate Limiting The rate limiter uses a **fixed window** and resets at the top of each minute. Every rate-limited `/v1` response includes standard rate-limit headers: - `x-ratelimit-limit`: your tier's requests-per-minute allowance - `x-ratelimit-remaining`: requests left in the current minute window - `x-ratelimit-reset`: Unix epoch seconds when the window resets 429 responses also include `Retry-After` (seconds until the window resets). Unlimited tiers receive no rate-limit headers. When you exceed the per-minute limit, the server responds with HTTP 429 and: ``` Retry-After: 60 ``` --- ## Endpoints ### Sports & Leagues #### GET /v1/sports List all sports and their leagues. **Tier:** Free **Response:** ```json { "success": true, "data": [ { "sport": "basketball", "leagues": [{ "leagueId": "nba", "name": "NBA" }] } ] } ``` --- #### GET /v1/leagues List all leagues, optionally filtered by sport. **Tier:** Free **Params:** `?sport=basketball` **Response:** ```json { "success": true, "meta": { "count": 4 }, "data": [ { "leagueId": "nba", "name": "NBA", "sport": "basketball" } ] } ``` --- #### GET /v1/leagues/:leagueId Get a single league by ID. **Tier:** Free **League IDs:** `nba`, `nfl`, `mlb`, `nhl`, `ncaa_basketball`, `ncaa_football`, `soccer_epl`, `soccer_mls`, `soccer_world_cup` --- #### GET /v1/leagues/:leagueId/rankings Get the most recent poll rankings for a league. - **`ncaa_basketball`**: `?poll=ap` or `?poll=coaches` - **`ncaa_football`**: `?poll=cfp` (default), `?poll=ap`, or `?poll=coaches` **Tier:** Free **Response:** ```json { "success": true, "data": { "leagueId": "ncaa_basketball", "pollType": "ap", "season": "2025-26", "fetchedAt": "2026-03-31T06:00:00Z", "rankings": [ { "rank": 1, "teamName": "Duke", "record": "32-4", "points": 1580 }, { "rank": 2, "teamName": "Arizona", "record": "30-5", "points": 1490 } ] } } ``` NCAAF rankings include a `conference` field on each entry. Updated twice daily. --- ### Events & Scores #### Stub Events Some events appear in the API before stats data has been ingested for the game. These **stub events** are seeded from odds data and carry an `isStub: true` flag. Stubs are enriched with team IDs when available and are automatically replaced by canonical events once stats data is ingested — the `eventId` remains stable through this transition. A daily schedule sync (every 6 hours) and a periodic enrichment job (every 30 minutes) keep stubs up to date. Client code should treat `isStub: true` events as fully usable but potentially missing some boxscore and play-by-play data. #### GET /v1/events List events with filtering and pagination. **Tier:** Free **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Filter by league ID | | sport | string | — | Filter by sport | | date | string | — | Exact date (YYYY-MM-DD) | | from | string | — | Range start (ISO 8601) | | to | string | — | Range end (ISO 8601) | | status | string | — | `scheduled`, `in_progress`, or `final` | | limit | number | 25 | Max 100 | | page | number | 1 | Page number | Final events also include explicit outcome fields so clients do not need to derive winners from the raw scores: - `outcome`: `home_win`, `away_win`, or `draw` - `winnerTeamId`, `winnerTeamName` - `loserTeamId`, `loserTeamName` **Response:** ```json { "success": true, "meta": { "count": 14, "total": 142, "page": 1, "pages": 6 }, "data": [ { "eventId": "nba-ev-311286", "leagueId": "nba", "sport": "basketball", "homeTeamName": "Boston Celtics", "awayTeamName": "Los Angeles Lakers", "startTime": "2026-03-09T23:30:00.000Z", "status": "final", "isStub": false, "scores": { "home": 112, "away": 105 }, "outcome": "home_win", "winnerTeamId": "nba-bos", "winnerTeamName": "Boston Celtics", "loserTeamId": "nba-lal", "loserTeamName": "Los Angeles Lakers" } ] } ``` --- #### GET /v1/events/live Get all in-progress events with live scores (capped at 500 results). **Tier:** Free **Params:** `?league=nba` In-progress events include `period` and `clock` fields. --- #### GET /v1/events/today Get all events on the current `America/New_York` calendar day (capped at 500 results). **Tier:** Free **Params:** `?league=nba` --- #### GET /v1/events/:eventId Get full details for a single event. **Tier:** Free For final events, the response includes `outcome`, `winnerTeamId`, `winnerTeamName`, `loserTeamId`, and `loserTeamName`. --- #### GET /v1/events/:eventId/play-by-play Get play-by-play data for a specific event. **Tier:** All plans --- #### GET /v1/leagues/:leagueId/scores Get game scores for a league on a specific date. **Tier:** Free **Params:** `?date=2026-03-20` (defaults to today) Returned event objects use the same schema as `/v1/events`. When a game is final, each event also includes `outcome`, `winnerTeamId`, `winnerTeamName`, `loserTeamId`, and `loserTeamName`. --- #### GET /v1/leagues/:leagueId/standings Get current standings for a league. **Tier:** Free **Params:** `?conference=Eastern&division=Atlantic` --- ### Teams & Players #### GET /v1/teams List all teams, optionally filtered by league. **Tier:** Free **Params:** `?league=nba` **Response:** ```json { "success": true, "data": [ { "teamId": "nba-bos", "name": "Boston Celtics", "abbreviation": "BOS", "leagueId": "nba" } ], "meta": { "count": 1, "league": "nba" } } ``` --- #### GET /v1/leagues/:leagueId/teams List all teams in a league. **Tier:** Free **Response:** ```json { "success": true, "meta": { "count": 30, "league": "nba" }, "data": [ { "teamId": "nba-bos", "name": "Boston Celtics", "abbreviation": "BOS", "leagueId": "nba" } ] } ``` --- #### GET /v1/teams/:teamId Get detailed info for a team. **Tier:** Free --- #### GET /v1/teams/:teamId/roster Get the current roster for a team. **Tier:** Free **Response includes:** Array of `{ playerId, name, position, number }` --- #### GET /v1/teams/:teamId/injuries Get the injury report for a team. Refreshed every 6 hours league-wide, and every 30 minutes for teams with a game starting within the next 24 hours. **Coverage: NFL, NBA and NHL only.** MLB, NCAAF, NCAAB and soccer teams return 404 with an explicit "not available for this league" message — that is a coverage boundary, not a gap in a covered league. A covered team with nobody hurt returns 200 with an empty `players` array. **Tier:** All plans **Response includes:** Array of `{ playerId, name, position, status, injury, returnDate, firstSeenAt, lastChangedAt }`. `firstSeenAt` is when this player first appeared on the report; `lastChangedAt` is when their status, injury description, or return date last changed. Both are observation timestamps recorded by MoneyLine's ingestion pipeline (ISO 8601) — use them to prove a status was known before a given kickoff time. `playerId` links entries to `/v1/teams/:teamId/injuries/history` change events. --- #### GET /v1/teams/:teamId/injuries/history Injury report history for a team. Every status change is recorded permanently as a change event (history accrues from the feature's launch forward). Same coverage as the report itself: NFL, NBA and NHL. **Tier:** All plans **Params:** `?asOf=2026-08-12T19:00:00Z` (optional), `?limit=100` (optional, max 500, ignored with asOf) **Response includes:** With `asOf` (must carry an explicit timezone, e.g. `Z`) — the reconstructed injury report as it stood at that moment: `{ teamId, leagueId, asOf, players: [{ playerId, name, position, status, injury, returnDate, lastChangedAt }] }` (`players` is empty for moments before history began accruing). Without `asOf` — the change log, most recent first: `{ teamId, leagueId, changes: [{ changeType: "added"|"updated"|"removed", changedAt, playerId, name, position, status, previousStatus?, injury, returnDate, teamId, leagueId }] }`. --- #### GET /v1/teams/:teamId/stats Get aggregated stats for a team. **Tier:** Free **Params:** `?season=2025` --- #### GET /v1/teams/:teamId/schedule Get the schedule (past and upcoming) for a team. **Tier:** Free --- #### GET /v1/players List players, optionally filtered by league or team. **Tier:** Free **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Filter by league ID | | team | string | — | Filter by team ID | | limit | number | 50 | Max 100 | | page | number | 1 | Page number | **Response:** ```json { "success": true, "data": [ { "playerId": "nba-p-2544", "playerName": "LeBron James", "teamId": "nba-lal", "leagueId": "nba", "position": "SF" } ], "meta": { "count": 1, "total": 1, "page": 1, "pages": 1, "league": "nba" } } ``` --- #### GET /v1/players/:playerId Get detailed info for a player. **Tier:** Free **Notes:** - Response includes all stored player fields plus `teamAbbr` (e.g. `"NYK"`) and `teamName` (e.g. `"New York Knicks"`) looked up from the teams collection via the player's `teamId`. --- #### GET /v1/players/:playerId/stats Get stats for a player. **Tier:** Free **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | type | string | season | `season` or `game` | | season | string | current | Season year (e.g. "2025" or "2025-26") | | eventId | string | — | For `type=game` only. Filter to one exact event ID | | date | string | — | For `type=game` only. Exact date (`YYYY-MM-DD`) | | from | string | — | For `type=game` only. Range start (`YYYY-MM-DD` or ISO 8601) | | to | string | — | For `type=game` only. Range end (`YYYY-MM-DD` or ISO 8601) | **Notes:** - If `type` is omitted, the API returns season summaries. - `type=season` rejects `date`, `from`, and `to`. - `type=season` rejects `eventId`. - `date` cannot be combined with `from` or `to`. - `type=career` is not currently supported. - Player stats are refreshed daily. - `type=game` game log entries include `gameDateDisplay` (formatted date string, e.g. `"Mar 14"`) and `opponentAbbr` (opponent team abbreviation, e.g. `"LAL"`) for display purposes. --- #### GET /v1/players/:playerId/hit-rates Get hit rates for a player against a specific prop line — how often the player has cleared the line in recent games. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | **Required.** Prop market key (e.g. `player_points`, `player_assists`, `player_rebounds`) | | line | number | — | Required for standard line-based markets. Omit it for yes/no or over-only markets; the API will treat those as `line=0` | **Notes:** - Binary yes/no markets (e.g. `player_anytime_td`, `player_anytime_goal_scorer`) use `line=0`. The hit rate reflects how often the player scored/scored a goal in recent games. - Football touchdown markets count touchdowns the player **scored** — rushing and receiving only. A quarterback throwing a touchdown pass does not count, since the receiver is the scorer. This applies to `player_anytime_td` and `player_tds_over`; use `player_pass_rush_reception_tds` for the market that includes passing touchdowns. - Some markets cannot be computed from boxscore data. These return `hitRateSupported: false` plus a `reason` field and no `hitRates` object. Use `GET /v1/player-props/markets` to see which markets are supported. **Response (standard market):** ```json { "success": true, "data": { "playerId": "nba-p-4065648", "market": "player_points", "line": 14.5, "direction": "over", "hitRates": { "L5": { "games": 5, "hits": 4, "rate": 0.800 }, "L10": { "games": 10, "hits": 6, "rate": 0.600 }, "L25": { "games": 25, "hits": 15, "rate": 0.600 }, "season": { "games": 62, "hits": 37, "rate": 0.597 } } } } ``` **Response (uncomputable market):** ```json { "success": true, "data": { "playerId": "nfl-p-12345", "market": "player_first_td", "hitRateSupported": false, "reason": "Market depends on in-game ordering or proprietary formulas — not computable from boxscore totals." } } ``` --- #### GET /v1/players/trending Get players sorted by hit rate, paired with their best available prop line and odds. Designed for discovery surfaces that rank players by recent performance against the market. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | **Required.** League ID (`nba`, `nfl`, `mlb`, `nhl`) | | market | string | — | **Required.** Prop market key (e.g. `player_points`) | | sortBy | string | `l5` | Hit-rate window to sort by: `l5`, `l10`, `l25`, or `season` | | direction | string | `over` | `over` or `under` | | limit | number | 25 | Max 50 | | page | number | 1 | Page number | **Response:** ```json { "success": true, "data": [ { "playerId": "nba-p-4065648", "playerName": "Coby White", "teamId": "nba-cha", "teamAbbr": "CHA", "teamName": "Charlotte Hornets", "position": "PG", "eventId": "nba-ev-311286", "market": "player_points", "direction": "over", "bestLine": 14.5, "bestOdds": -108, "bookmakerName": "DraftKings", "bookmakerId": "draftkings", "sourceType": "sportsbook", "hitRates": { "L5": { "games": 5, "hits": 4, "rate": 0.800 }, "L10": { "games": 10, "hits": 6, "rate": 0.600 }, "L25": { "games": 25, "hits": 15, "rate": 0.600 }, "season": { "games": 62, "hits": 37, "rate": 0.597 } } } ], "meta": { "count": 25, "total": 87, "page": 1, "pages": 4, "league": "nba", "market": "player_points", "sortBy": "l5", "direction": "over" } } ``` --- #### GET /v1/players/trends Get each player's highest-profit active prop trend over a rolling game window. This endpoint is designed for trends/discovery pages that need matchup context, the best current bet, and flat-stake profit in one response. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Optional league ID filter (`nba`, `nfl`, `mlb`, `nhl`, `ncaa_basketball`, `ncaa_football`, `soccer_epl`, `soccer_mls`, `soccer_world_cup`) | | window | number | `25` | Rolling game window size. Integer from `1` to `100` | | bookmaker | string | — | Optional bookmaker key or display name filter | | sourceType | string | `all` | `all`, `sportsbook`, `dfs`, or `exchange` | | limit | number | `25` | Max 50 | | page | number | `1` | Page number | **Response:** ```json { "success": true, "data": [ { "player": { "playerId": "nba-p-4065648", "name": "Coby White", "teamId": "nba-chi", "teamAbbr": "CHI", "teamName": "Chicago Bulls", "team": "CHI", "matchup": "CHI @ NYK" }, "eventId": "nba-ev-311286", "leagueId": "nba", "bet": { "market": "player_points", "marketName": "Player Points", "direction": "under", "line": 24.5, "price": 125, "bookmakerId": "bovada", "bookmakerName": "Bovada", "sourceType": "sportsbook" }, "sampleSize": 25, "performance": { "wins": 20, "losses": 5, "pushes": 0, "hitRate": 0.8, "stake": 100 }, "profit": 2000 } ], "meta": { "count": 25, "total": 73, "page": 1, "pages": 3, "window": 25, "stake": 100, "league": "nba", "sourceType": "all" } } ``` --- #### GET /v1/players/:playerId/analysis Get an integrated analysis for a player: current best bet, game-by-game chart data, and hit rates in a single response. Designed for player detail screens. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | **Required.** Prop market key (e.g. `player_points`) | | window | string | `l5` | Time window for the chart: `l5`, `l10`, `l25`, or `season` | | season | string | current season when `window=season` | Optional season filter for the chart + hit rates (e.g. `2025` or `2025-26`) | | eventId | string | — | Optional exact event filter for chart + hit rates | | date | string | — | Optional exact game date filter for chart + hit rates (`YYYY-MM-DD`) | | from | string | — | Optional range start for chart + hit rates (`YYYY-MM-DD` or ISO 8601) | | to | string | — | Optional range end for chart + hit rates (`YYYY-MM-DD` or ISO 8601) | **Notes:** - `date` cannot be combined with `from` or `to`. - With no game-log filters, the endpoint behaves like a fast current player detail view. - `window=season` returns the full selected season's game logs in the chart, not just L25. - If `season`, `eventId`, `date`, `from`, or `to` are provided, the chart and hit rates are computed from that filtered game-log set. - `bestBet` still reflects the current best available line for the player + market, even when historical chart filters are applied. **Response:** ```json { "success": true, "data": { "player": { "playerId": "nba-p-4065648", "playerName": "Coby White", "teamId": "nba-cha", "leagueId": "nba", "position": "PG" }, "currentEvent": { "eventId": "nba-ev-311286", "homeTeamId": "nba-cha", "awayTeamId": "nba-nyk", "startTime": "2026-03-26T23:10:00.000Z" }, "bestBet": { "market": "player_points", "line": 14.5, "odds": -108, "bookmakerId": "draftkings", "bookmakerName": "DraftKings", "sourceType": "sportsbook" }, "hitRates": { "L5": { "games": 5, "hits": 4, "rate": 0.800 }, "L10": { "games": 10, "hits": 6, "rate": 0.600 }, "L25": { "games": 25, "hits": 15, "rate": 0.600 }, "season": { "games": 62, "hits": 37, "rate": 0.597 } }, "chart": { "window": "l5", "line": 14.5, "filters": { "season": "2025-26" }, "games": [ { "gameDate": "2026-03-14T00:00:00.000Z", "gameDateDisplay": "Mar 14", "opponent": "San Antonio Spurs", "opponentAbbr": "SAS", "value": 18, "hit": true }, { "gameDate": "2026-03-17T00:00:00.000Z", "gameDateDisplay": "Mar 17", "opponent": "Miami Heat", "opponentAbbr": "MIA", "value": 24, "hit": true }, { "gameDate": "2026-03-19T00:00:00.000Z", "gameDateDisplay": "Mar 19", "opponent": "Orlando Magic", "opponentAbbr": "ORL", "value": 27, "hit": true }, { "gameDate": "2026-03-21T00:00:00.000Z", "gameDateDisplay": "Mar 21", "opponent": "Memphis Grizzlies", "opponentAbbr": "MEM", "value": 12, "hit": false }, { "gameDate": "2026-03-24T00:00:00.000Z", "gameDateDisplay": "Mar 24", "opponent": "Sacramento Kings", "opponentAbbr": "SAC", "value": 27, "hit": true } ] } } } ``` --- ### Odds & Bookmakers #### GET /v1/odds Browse odds with filtering. Returns odds from sportsbooks, DFS platforms, and exchanges by default. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Filter by league ID | | market | string | — | `moneyline`, `spread`, `total`, or any supported prop market such as `player_points` | | bookmaker | string | — | Filter by bookmaker key or name | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | limit | number | 25 | Max 50 | | page | number | 1 | Page number | **Notes:** - Default venue ordering is sportsbook, then DFS, then exchange, then unknown. - All plans can access the full returned venue set. - `sourceType`, `bookmaker`, and `market` filters are applied before the response is returned. **Response:** ```json { "success": true, "meta": { "count": 25, "page": 1 }, "data": [ { "eventId": "nba-ev-311310", "leagueId": "nba", "bookmakers": [ { "bookmakerId": "draftkings", "bookmakerName": "DraftKings", "sourceRegion": "us", "sourceType": "sportsbook", "markets": [...] }, { "bookmakerId": "prizepicks", "bookmakerName": "PrizePicks", "sourceRegion": "us_dfs", "sourceType": "dfs", "markets": [...] }, { "bookmakerId": "sporttrade", "bookmakerName": "Sporttrade", "sourceRegion": "us_ex", "sourceType": "exchange", "markets": [...] } ] } ] } ``` --- #### GET /v1/events/:eventId/odds Get odds for a specific event. Includes a `summary` object with fair odds (no-vig), best odds, and average odds per market and outcome for cross-book comparison. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | market | string | — | Optional exact market filter | | market | string | — | Optional exact market filter such as `moneyline`, `spread`, or `total` | **Response includes:** ```json { "summary": { "moneyline": [ { "name": "Charlotte Hornets", "fairOdds": -109.7, "bestOdds": -113, "avgOdds": -118.8 }, { "name": "New York Knicks", "fairOdds": 109.7, "bestOdds": 115, "avgOdds": 102 } ], "spread": [ ... ], "total": [ ... ] }, "bookmakers": [ ... ] } ``` --- #### GET /v1/events/:eventId/odds-history Consensus odds history for a specific event — one snapshot per refresh tick, showing how the line has moved over time. Each snapshot contains one row per `(marketType, outcome, point)` with the cross-book **average**, **best**, and **no-vig fair** price at that moment. Game markets (moneyline/spread/total) and player props are both included. Snapshots are written on a tiered cadence based on time-to-game so storage stays small: | Time to event | Snapshot interval | |---|---| | > 7 days | every 24 hours | | 24 hours – 7 days | every 12 hours | | 3 – 24 hours | every 60 minutes | | < 3 hours | every 20 minutes | Consensus is computed across sportsbooks and exchanges only — DFS operators are excluded because their prices are indicative, not tradeable. History for an event is automatically deleted shortly after the game goes final. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | Optional exact market filter (e.g. `moneyline`, `spread`, `total`, `player_points`) | | playerId | string | — | Optional canonical player ID — returns only rows for that player's props within the event | | from | ISO date | — | Return snapshots at or after this time | | to | ISO date | — | Return snapshots at or before this time | | limit | integer | 500 | Max snapshots returned (capped at 2000) | Prop-market rows include `player` (the player name) and `playerId` (the canonical ID used elsewhere in the API) when resolvable, alongside the `outcome` side (`Over`/`Under`/etc). **Response:** ```json { "eventId": "nba-ev-lakers-celtics-2026-04-15t1900", "count": 14, "snapshots": [ { "eventId": "nba-ev-lakers-celtics-2026-04-15t1900", "leagueId": "nba", "sport": "basketball", "snapshotAt": "2026-04-08T19:00:00.000Z", "markets": [ { "marketType": "moneyline", "outcome": "Los Angeles Lakers", "avgPrice": -118, "avgImpliedProb": 0.5413, "bestPrice": -112, "bestBook": "fanduel", "fairOdds": -109.7, "bookCount": 12 }, { "marketType": "spread", "outcome": "Boston Celtics", "point": 2.5, "avgPrice": -110, "avgImpliedProb": 0.5238, "bestPrice": -105, "bestBook": "draftkings", "fairOdds": -100.9, "bookCount": 12 } ] } ] } ``` Snapshots are sorted ascending by `snapshotAt`, so the first entry is the opening consensus line captured for that event and the last is the most recent. --- #### GET /v1/players/:playerId/odds-history Cross-event consensus odds history for a single player. Each snapshot's `markets` array contains only rows belonging to that player, so the response plugs directly into a player detail page that charts how the market has moved on their props over time. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | Optional exact prop market filter (e.g. `player_points`, `player_rebounds`) | | eventId | string | — | Optional exact event filter — scope to a single game | | from | ISO date | — | Return snapshots at or after this time | | to | ISO date | — | Return snapshots at or before this time | | limit | integer | 500 | Max snapshots returned (capped at 2000) | **Response:** same shape as `/v1/events/:eventId/odds-history`, but the top-level object is keyed by `playerId` instead of `eventId`, and every row in every snapshot will have `playerId` equal to the requested ID. --- #### GET /v1/odds/bookmakers List all tracked sportsbooks, DFS platforms, and exchanges. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | market | string | — | Optional exact market filter | **Response:** Standard success envelope with `data` as an array of `{ bookmakerId, name, sourceType, sourceRegion }` ```json { "success": true, "data": [ { "bookmakerId": "draftkings", "name": "DraftKings", "sourceType": "sportsbook", "sourceRegion": "us" }, { "bookmakerId": "prizepicks", "name": "PrizePicks", "sourceType": "dfs", "sourceRegion": "us_dfs" }, { "bookmakerId": "espnbet", "name": "ESPN BET", "sourceType": "sportsbook", "sourceRegion": "us2" }, { "bookmakerId": "sporttrade", "name": "Sporttrade", "sourceType": "exchange", "sourceRegion": "us_ex" } ], "meta": { "count": 4 } } ``` **Bookmaker key aliases:** Some bookmakers have alternate keys that map to the canonical ID — e.g. `hardrockbet` is an alias for `hardrock_bet`. Either key is accepted in filter params. --- ### Player Props Player props are normalized onto the same canonical event namespace used by Events and Odds. The `eventId` returned by `/v1/player-props` is always the join-safe event identifier for `/v1/events`, `/v1/events/:eventId/odds`, and `/v1/events/:eventId/player-props`. Each props event object also includes: - `canonicalEventId` (same value as `eventId`, explicit join key), - `homeTeamName`, - `awayTeamName`, - `startTime`. Every player entry in a props response includes a `playerId` that is the same canonical ID used by `/v1/players/:playerId` and `/v1/players/:playerId/stats`. This means you can filter props by player ID and join prop lines directly against game logs or season stats without any name-matching on your end. Edge and best-bet responses also use that same canonical `eventId`. If you discover a bet via `/v1/edge`, `/v1/edge/value`, `/v1/edge/ev`, `/v1/edge/arbitrage`, or `/v1/best-bets`, you can use the returned `eventId` directly with the event, odds, player-props, and event-edge endpoints. #### GET /v1/player-props Browse player props grouped by event, player, market, and line. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Filter by league ID | | market | string | — | Exact prop market key such as `player_points`, `player_pass_yds`, or `pitcher_strikeouts` | | player | string | — | Case-insensitive player-name substring filter | | playerId | string | — | Exact internal player ID (e.g. `nba-p-4065648`). Faster and unambiguous — use this instead of `player` when you have the ID | | bookmaker | string | — | Filter by bookmaker key or name | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | limit | number | 25 | Max 50 | | page | number | 1 | Page number | **Notes:** - Results are paginated at the event level. - `eventId` is canonicalized to the Events namespace; use it directly to join with `/v1/events`. - `canonicalEventId` is provided as an explicit stable join key (same value as `eventId`). - Event-level metadata (`homeTeamName`, `awayTeamName`, `startTime`) is included for defensive matching and display. - Each player entry includes a `playerId` matching the ID returned by `/v1/players` and `/v1/players/:playerId/stats`. - Each player entry includes `teamAbbr` (e.g. `"NYK"`) and `teamName` (e.g. `"New York Knicks"`) for display purposes. - Players that could not be resolved onto a canonical `playerId` are omitted from public player-props responses so every returned player remains joinable. - `playerId` and `player` can be combined with `market`, `bookmaker`, and `sourceType`. **Response:** ```json { "success": true, "meta": { "count": 1, "page": 1 }, "data": [ { "eventId": "nba-ev-311310", "canonicalEventId": "nba-ev-311310", "leagueId": "nba", "sport": "basketball", "homeTeamName": "Boston Celtics", "awayTeamName": "New York Knicks", "startTime": "2026-03-26T23:10:00.000Z", "fetchedAt": "2026-03-25T00:10:00.000Z", "players": [ { "playerName": "Jayson Tatum", "playerId": "nba-p-4065648", "teamAbbr": "BOS", "teamName": "Boston Celtics", "markets": [ { "marketType": "player_points", "marketName": "Points", "format": "over_under", "isAlternate": false, "lines": [ { "point": 29.5, "offers": [ { "bookmakerId": "fanduel", "bookmakerName": "FanDuel", "sourceType": "sportsbook", "sourceRegion": "us", "selection": "Over", "price": 120, "impliedProbability": 0.455 } ] } ] } ] } ] } ] } ``` --- #### GET /v1/events/:eventId/player-props Get player props for a specific event using an `eventId` from `/v1/events`. The endpoint resolves legacy/mismatched stored props IDs server-side and returns a response keyed to the requested canonical event ID. Each line entry includes a `summary` with fair odds, best odds, and average odds per selection (over/under) for easy cross-book comparison. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | Exact prop market key | | player | string | — | Case-insensitive player-name substring filter | | playerId | string | — | Exact internal player ID | | bookmaker | string | — | Filter by bookmaker key or name | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | **Line summary example:** ```json { "point": 14.5, "summary": { "over": { "fairOdds": 103, "bestOdds": -108, "avgOdds": -114.3 }, "under": { "fairOdds": -103, "bestOdds": -110, "avgOdds": -121.8 } }, "offers": [ ... ] } ``` --- #### GET /v1/player-props/markets List supported player prop markets by league. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Optional league ID to return one league only | **Response:** Array of `{ leagueId, sport, markets[] }` where each market includes `marketType`, `marketName`, `format`, `isAlternate`, and `supportsPoint`. --- ### Edge Data & Analysis #### GET /v1/edge Browse edge opportunities (arbitrage, value bets, positive EV). **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | type | string | — | `arbitrage`, `value`, or `ev` | | league | string | — | Filter by league ID | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | market | string | — | Optional exact market filter | | minProfit | number | — | Min profit % (arbitrage only) | | minEdge | number | — | Min edge % (value bets only) | | limit | number | 25 | Max 50 | | page | number | 1 | Page number | **sourceType behavior for edge endpoints:** - `sportsbook` — sportsbook-only arbs + sportsbook value/EV bets. - `dfs` — DFS value/EV bets only. DFS operators are excluded from arbitrage calculations because their prices are indicative, not tradeable lines. - `exchange` — exchange value/EV bets, plus exchange-only arbitrage from approved venues when the prices pass sanity checks. - `all` (default) — all value/EV bets plus all arbitrage edges, including `mixed` sportsbook/exchange arbitrage when present. `GET /v1/edge` returns an array of event-grouped documents: ```json [ { "eventId": "nba-ev-311286", "leagueId": "nba", "sport": "basketball", "calculatedAt": "2026-03-26T14:05:00.000Z", "edges": [ ... ] } ] ``` **Arbitrage edge payload:** ```json { "type": "arbitrage", "venueType": "sportsbook", "market": "moneyline", "arbitrage": { "books": [ { "bookmaker": "DraftKings", "bookmakerId": "draftkings", "sourceType": "sportsbook", "sourceRegion": "us", "outcome": "Team A", "odds": -110, "stake": 523.81 } ], "profitPct": 2.5, "guaranteedProfit": 25.0, "totalStake": 1000 } } ``` **Value edge payload:** ```json { "type": "value", "sourceType": "sportsbook", "sourceRegion": "us", "market": "moneyline", "outcome": "Team A", "valueBet": { "bookmaker": "FanDuel", "bookmakerId": "fanduel", "odds": +145, "impliedProb": 0.408, "modelProb": 0.472, "edgePct": 6.4, "kellyCriterion": 0.108 } } ``` --- #### GET /v1/edge/arbitrage Get arbitrage opportunities. **Tier:** All plans **Params:** `?league=nba&minProfit=2&sourceType=all` Default `sourceType=all`. Use `sourceType=sportsbook` or `exchange` to narrow to one venue type. DFS operators are excluded from arbitrage regardless of `sourceType` because their prices are indicative, not tradeable lines. --- #### GET /v1/edge/value Get value bets where odds exceed fair probability. **Tier:** All plans **Params:** `?league=nba&minEdge=5&sourceType=all` Default `sourceType=all`. Edges are emitted as `type: 'value'` with a `valueBet` field. --- #### GET /v1/edge/ev Get positive expected value bets (below the value-bet edge threshold). **Tier:** All plans **Params:** `?league=nba&sourceType=all` --- #### GET /v1/events/:eventId/edge Get edge data for a specific event. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | market | string | — | Optional exact market filter | --- ### Best Bets Best bets surface the most favorable available line per market outcome across all tracked venues. In American odds, a numerically higher number is always better for the bettor (e.g. -100 is better than -110, +150 is better than +120). Best-bets endpoints can include both game markets and player prop markets. Separately, every offer in `/v1/player-props` responses includes an `isBest` field indicating whether it is the top-priced offer for that selection at that line. #### GET /v1/best-bets Browse best available lines across all supported markets. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | league | string | — | Filter by league ID | | market | string | — | Filter by market type (e.g. `moneyline`, `spread`, `total`, `player_points`) | | bookmaker | string | — | Return the best line from a specific venue only | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | | limit | number | 25 | Max 50 | | page | number | 1 | Page number | **Notes:** - Without `bookmaker` or `sourceType` filters, results are served from a precomputed snapshot refreshed after each odds fetch. - When `bookmaker` or `sourceType` is provided, best odds are computed on-demand scoped to the filtered set. **Response:** ```json { "success": true, "meta": { "count": 14, "page": 1 }, "data": [ { "eventId": "nba-ev-311310", "leagueId": "nba", "sport": "basketball", "calculatedAt": "2026-03-25T00:10:00.000Z", "markets": [ { "marketType": "moneyline", "outcomes": [ { "name": "Boston Celtics", "bestOdds": -115, "bookmakerId": "draftkings", "bookmakerName": "DraftKings", "sourceType": "sportsbook", "sourceRegion": "us" }, { "name": "Los Angeles Lakers", "bestOdds": 105, "bookmakerId": "fanduel", "bookmakerName": "FanDuel", "sourceType": "sportsbook", "sourceRegion": "us" } ] }, { "marketType": "spread", "outcomes": [ { "name": "Boston Celtics", "point": -7.5, "bestOdds": -108, "bookmakerId": "betmgm", "bookmakerName": "BetMGM", "sourceType": "sportsbook", "sourceRegion": "us" } ] } ] } ] } ``` --- #### GET /v1/events/:eventId/best-bets Get best available lines for a specific event. **Tier:** All plans **Params:** | Param | Type | Default | Description | |-------|------|---------|-------------| | market | string | — | Filter by market type | | bookmaker | string | — | Return best line from a specific sportsbook only | | sourceType | string | `all` | `sportsbook`, `dfs`, `exchange`, or `all` | --- ## Quick-Start Examples ### Fetch today's NBA games ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/today?league=nba" ``` ### Get live scores ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/live" ``` ### Get odds for a game ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/nba-ev-311310/odds" ``` ### Get best available lines across all NFL games ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/best-bets?league=nfl" ``` ### Get DraftKings' best lines for an NBA game ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/nba-ev-311310/best-bets?bookmaker=draftkings" ``` ### Get best sportsbook lines only ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/best-bets?league=nba&sourceType=sportsbook" ``` ### Get arbitrage opportunities ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/edge/arbitrage?league=nba&minProfit=1" ``` ### Get all arbitrage opportunities (including sane mixed arbs) ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/edge/arbitrage?league=nba&sourceType=all" ``` ### List DFS platforms only ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/odds/bookmakers?sourceType=dfs" ``` ### List exchanges only ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/odds/bookmakers?sourceType=exchange" ``` ### Get exchange odds for a game ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/nba-ev-311310/odds?sourceType=exchange" ``` ### Get player props for a game using the same event ID ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/events/nba-ev-311310/player-props?market=player_points&sourceType=all" ``` ### List supported NBA player prop markets ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/player-props/markets?league=nba" ``` ### Get team roster ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/teams/nba-bos/roster" ``` ### Get player season stats ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/nba-p-12345/stats?type=season" ``` ### Get trending players by hit rate ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/trending?league=nba&market=player_points&sortBy=l5" ``` ### Get top player trends by flat-stake profit ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/trends?league=nba&window=25&limit=10" ``` ### Get hit rates for a player against a line ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/nba-p-12345/hit-rates?market=player_points&line=14.5" ``` ### Get integrated player analysis with chart data ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/nba-p-12345/analysis?market=player_points&window=l5" ``` ### Get full-season player analysis chart data ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/nba-p-12345/analysis?market=player_points&window=season&season=2025-26" ``` ### Get date-range player analysis chart data ```bash curl -H "x-api-key: ml_live_YOUR_KEY" \ "https://mlapi.bet/v1/players/nba-p-12345/analysis?market=player_points&window=season&from=2026-01-01&to=2026-02-01" ``` ### Use MoneyLine AI with auto-infer ```bash curl -X POST https://mlapi.bet/v1/ai/chat \ -H "content-type: application/json" \ -H "x-api-key: ml_live_YOUR_KEY" \ -d '{ "scope": "small", "responseFormat": "hybrid", "messages": [ { "role": "user", "content": "What is the best bet that I can do today? Only show me FanDuel." } ] }' ``` --- ## Endpoint Summary | Method | Path | Access | Description | |--------|------|--------|-------------| | GET | /v1/sports | All plans | List sports and leagues | | GET | /v1/leagues | All plans | List leagues | | GET | /v1/leagues/:leagueId | All plans | Get single league | | GET | /v1/events | All plans | List events (paginated) | | GET | /v1/events/live | All plans | In-progress events | | GET | /v1/events/today | All plans | Today's events | | GET | /v1/events/:eventId | All plans | Single event | | GET | /v1/events/:eventId/play-by-play | All plans | Play-by-play data | | GET | /v1/leagues/:leagueId/scores | All plans | Scores by date | | GET | /v1/leagues/:leagueId/standings | All plans | League standings | | GET | /v1/leagues/:leagueId/teams | All plans | Teams in league | | GET | /v1/teams/:teamId | All plans | Team details | | GET | /v1/teams/:teamId/roster | All plans | Team roster | | GET | /v1/teams/:teamId/injuries | All plans | Injury report with observation timestamps | | GET | /v1/teams/:teamId/injuries/history | All plans | Injury change log + point-in-time reconstruction | | GET | /v1/teams/:teamId/stats | All plans | Team stats | | GET | /v1/teams/:teamId/schedule | All plans | Team schedule | | GET | /v1/players/:playerId | All plans | Player details | | GET | /v1/players/:playerId/stats | All plans | Player stats by season, exact event, or game/date range | | GET | /v1/players/:playerId/hit-rates | All plans | Hit rates (L5/L10/L25/season) against a prop line | | GET | /v1/players/:playerId/analysis | All plans | Integrated player detail: best bet + hit rates + chart data, with optional season/date/event filters | | GET | /v1/players/trending | All plans | Players sorted by hit rate with best available line | | GET | /v1/players/trends | All plans | Highest-profit active player prop trend per player over a rolling window | | GET | /v1/odds | All plans | Browse odds | | GET | /v1/events/:eventId/odds | All plans | Event odds | | GET | /v1/events/:eventId/odds-history | All plans | Consensus odds history (line movement) for an event | | GET | /v1/players/:playerId/odds-history | All plans | Consensus odds history for a player's props across events | | GET | /v1/odds/bookmakers | All plans | List bookmakers | | GET | /v1/player-props | All plans | Browse player props | | GET | /v1/events/:eventId/player-props | All plans | Event player props | | GET | /v1/player-props/markets | All plans | Supported player prop markets | | GET | /v1/best-bets | All plans | Best available line per outcome across all sportsbooks | | GET | /v1/events/:eventId/best-bets | All plans | Best lines for a specific event | | GET | /v1/edge | All plans | Browse edge data | | GET | /v1/edge/arbitrage | All plans | Arbitrage opportunities | | GET | /v1/edge/value | All plans | Value bets | | GET | /v1/edge/ev | All plans | Positive EV bets | | GET | /v1/events/:eventId/edge | All plans | Event edge data | | POST | /v1/ai/chat | All plans | MoneyLine-grounded AI responses and recommendations |