Public API reference · v1
Everything you need to build on Odds Vault
Discover sports, books, and market types. Check freshness. List events cheaply. Pull multi-book odds. Convert prices. Hand the whole contract to Claude Code.
10 endpointsBearer account key~60s refresh target≤180s source ageOpenAPI includedQuick start
Typical flow: status → catalog → events → odds (or event detail). Use the canonical host so Authorization is not dropped.
curl 'https://www.oddsvault.app/api/v1/status' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'curl 'https://www.oddsvault.app/api/v1/catalog' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'curl 'https://www.oddsvault.app/api/v1/events?sport=nba&limit=50' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'curl 'https://www.oddsvault.app/api/v1/odds?sport=nfl&markets=h2h,spreads,totals&books=draftkings,pinnacle' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'Base URL: https://www.oddsvault.app. Odds responses are published current objects from private R2 — never a live scrape and never a Supabase snapshot fallback on the read path. Supabase is the control plane (auth, billing, refresh scheduling) only.
Authentication
All routes except OpenAPI need Authorization: Bearer ov_live_…. Create an account key from the dashboard after you are entitled (active subscription or trial — trial is full entitlement at your plan’s rate limits).
Header
Authorization: Bearer ov_live_YOUR_KEY- Store as
ODDSVAULT_API_KEY. Never commit secrets. 401missing/invalid key ·403subscription_required·429rate limited.- Successful paid responses include six
X-RateLimit-*headers (minute + hour windows). GET /api/v1/openapiis public (no auth, outside account limits) for agents and codegen.
Plans & rate limits
Both plans read the same private R2 current snapshots and apply the same ≤180s source-freshness rule. Fresh only raises how often you may poll — it does not buy a separate or newer feed.
| Plan | Stored tier | Minute | Hour | Notes |
|---|---|---|---|---|
| Standard | basic | 60 | 360 | $39/mo |
| Fresh | pro | 300 | 900 | $79/mo |
- “Unlimited” means no daily or monthly quota — both minute and hour windows always apply.
- 429 body codes:
minute_rate_limit_exceeded,hourly_rate_limit_exceeded, orunauthorized_rate_limit_exceeded(IP abuse before a valid key is presented). - Unauthorized abuse windows: 60/min and 360/hour.
Endpoint map
| Group | Endpoint | Purpose |
|---|---|---|
| Discovery | GET /api/v1/catalog | Full catalog Sports, books, markets (incl. planned props), product notes, counts. |
| Discovery | GET /api/v1/sports | Sports & leagues Keys for sport=. Optional group=soccer|basketball|… |
| Discovery | GET /api/v1/books | Sportsbooks Live books from the R2 index (keys for books=). Soft-empty if index unavailable. |
| Discovery | GET /api/v1/markets | Markets & prop types status / kind / queryable filters. Planned props included. |
| Discovery | GET /api/v1/status | Freshness status R2 index board: per-sport fresh/has_snapshot, ages, books. 503 if index missing; ok:false when all stale. |
| Events | GET /api/v1/events | Event index Lightweight board from R2: teams, commence, market/book counts — no quotes. |
| Events | GET /api/v1/events/{eventId} | Event detail Full multi-book markets for one event (R2). include_best=true optional. |
| Odds | GET /api/v1/odds | Odds snapshot Full multi-book pregame R2 payload for a sport (+ event_id / commence filters). |
| Utilities | GET /api/v1/convert | Odds convert American ↔ decimal ↔ implied, or two-way no-vig (home+away). |
| Utilities | GET /api/v1/openapi | OpenAPI 3.1 Machine-readable schema for agents/codegen. No auth required. |
Discovery
Prefer runtime catalogs over hardcoding keys. Planned prop types are listed so UIs and agents can plan ahead without inventing market keys.
GET /api/v1/catalog
curl 'https://www.oddsvault.app/api/v1/catalog' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'{
"api_version": "v1",
"product": {
"name": "Odds Vault Pregame API",
"refresh_target_seconds": 60,
"max_source_age_seconds": 180,
"live_odds": false
},
"sports": [{ "key": "nfl", "name": "NFL", "group": "american_football", "odds_supported": true }],
"books": [{ "key": "draftkings", "name": "DraftKings", "domain": "draftkings.com", "region": "us", "odds_supported": true }],
"markets": [{ "key": "h2h", "status": "available", "queryable": true, "family": "moneyline" }],
"queryable_markets": ["h2h", "spreads", "totals"],
"counts": {
"sports": 20,
"books": 1,
"markets": 21,
"markets_available": 3,
"markets_planned": 17
}
}GET /api/v1/sports
Optional group: american_football, basketball, baseball, hockey, soccer, combat, tennis.
curl 'https://www.oddsvault.app/api/v1/sports?group=soccer' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'GET /api/v1/books
Returns only the customer-visible live set from the published R2 index (not every catalog book). If the index is missing or predates the books field, the response is a soft empty list with note: "books_unavailable" — not 5xx.
curl 'https://www.oddsvault.app/api/v1/books' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'GET /api/v1/markets
Filters: status, kind, queryable. Available today: h2h, spreads, totals. Planned: 17 types (team totals, periods, player props). Only queryable keys may be passed to /odds / /events market filters.
curl 'https://www.oddsvault.app/api/v1/markets?kind=player_prop' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'Status / freshness
https://www.oddsvault.app/api/v1/statusDiagnostic board from the R2 index. Call this before polling. Skip sports where fresh: false (no snapshot or all sources older than max_source_age_seconds, ~180s).
- Valid index but every sport stale →
200withok: false(still useful diagnostics). - Missing or invalid index →
503(not a soft empty board). - Each sport row includes
has_snapshot,version,age_seconds,books, andsource_freshness.
curl 'https://www.oddsvault.app/api/v1/status' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'{
"ok": true,
"refresh_target_seconds": 60,
"max_source_age_seconds": 180,
"checked_at": "2026-07-18T18:42:12.000Z",
"summary": {
"sports_total": 20,
"sports_with_snapshot": 14,
"sports_fresh": 12,
"events_fresh": 184
},
"sports": [{
"sport": "nfl",
"fresh": true,
"has_snapshot": true,
"version": 42,
"generated_at": "2026-07-18T18:41:54.000Z",
"age_seconds": 18,
"event_count": 16,
"books": ["draftkings", "pinnacle"],
"source_freshness": [
{ "book": "draftkings", "scraped_at": "2026-07-18T18:41:54.000Z", "event_count": 10, "age_seconds": 18 },
{ "book": "pinnacle", "scraped_at": "2026-07-18T18:42:00.000Z", "event_count": 12, "age_seconds": 12 }
]
}]
}Events
Two-step pattern: list the board without quote bloat, then fetch one event (or filter /odds by event_id). Both routes read private R2 only.
GET /api/v1/events — index
Params: sport (recommended), books, markets, commence_after, commence_before, limit (default 100, max 500). Response includes limit, max_source_age_seconds, and per-event snapshot_version / age_seconds.
curl 'https://www.oddsvault.app/api/v1/events?sport=nba&limit=50' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'{
"sport": "nba",
"count": 2,
"total_matched": 2,
"truncated": false,
"limit": 100,
"max_source_age_seconds": 180,
"events": [{
"event_id": "evt_abc",
"sport": "nba",
"competition": null,
"commence_time": "2026-07-20T00:00:00Z",
"participants": {
"home": { "id": "bos", "name": "Boston Celtics" },
"away": { "id": "nyk", "name": "New York Knicks" }
},
"market_count": 3,
"market_families": ["moneyline", "spread", "total"],
"book_count": 8,
"books": ["draftkings", "fanduel"],
"snapshot_version": 42,
"age_seconds": 16
}]
}GET /api/v1/events/{eventId} — detail
Full markets for one event. Pass sport when known (faster). include_best=true adds a best-price rollup per selection across books (best_lines).
curl 'https://www.oddsvault.app/api/v1/events/evt_abc?sport=nba&include_best=true' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'Odds snapshot
https://www.oddsvault.app/api/v1/odds| Param | Required | Description |
|---|---|---|
sport | Yes | From /api/v1/sports |
books | No | From /api/v1/books |
markets | No | Queryable only: h2h, spreads, totals |
event_id | No | Restrict to one event |
commence_after / commence_before | No | ISO-8601 window on commence_time |
curl 'https://www.oddsvault.app/api/v1/odds?sport=nfl&markets=h2h,spreads,totals&books=draftkings,pinnacle' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'{
"sport": "nfl",
"snapshot_version": 42,
"generated_at": "2026-07-18T18:42:11.000Z",
"age_seconds": 14,
"oldest_source_age_seconds": 14,
"event_count": 1,
"books": ["draftkings", "pinnacle"],
"source_freshness": [
{ "book": "draftkings", "scraped_at": "2026-07-18T18:41:58.000Z", "event_count": 1, "age_seconds": 14 },
{ "book": "pinnacle", "scraped_at": "2026-07-18T18:42:00.000Z", "event_count": 1, "age_seconds": 12 }
],
"filters": {
"event_id": null,
"commence_after": null,
"commence_before": null,
"markets": ["h2h", "spreads", "totals"]
},
"events": [
{
"event_id": "evt_8f2a1c",
"sport": "nfl",
"competition": null,
"commence_time": "2026-09-10T00:20:00Z",
"participants": {
"home": { "id": "kansas_city_chiefs", "name": "Kansas City Chiefs" },
"away": { "id": "buffalo_bills", "name": "Buffalo Bills" }
},
"markets": [
{
"market_id": "ml_full",
"identity": { "family": "moneyline", "scope": "full_event", "line": null },
"selections": [
{
"side": "home",
"label": "Kansas City Chiefs",
"books": [{ "book": "draftkings", "price": -135, "price_decimal": 1.74 }]
}
]
}
]
}
]
}- Headers:
ETag,X-Odds-Snapshot-Version,X-Odds-Age-Seconds, plus rate-limit headers. SendIf-None-Matchfor304. - Only sources ≤
max_source_age_seconds(~180s) are included, with per-booksource_freshness. - Fresh empty board (validated snapshot, zero events after filters) →
200withevent_count: 0. No fresh requested book →503.
Convert
https://www.oddsvault.app/api/v1/convert- Single price: exactly one of
american,decimal,implied - Two-way no-vig:
home+away(American)
curl 'https://www.oddsvault.app/api/v1/convert?american=-110' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'
# Two-way no-vig
curl 'https://www.oddsvault.app/api/v1/convert?home=-110&away=-110' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'OpenAPI
https://www.oddsvault.app/api/v1/openapiPublic OpenAPI 3.1 JSON — no API key. Point Claude Code, SDK generators, or Postman at this URL.
curl 'https://www.oddsvault.app/api/v1/openapi'Errors
| Status | Meaning | Typical causes |
|---|---|---|
400 | Bad request |
|
401 | Unauthorized |
|
403 | Subscription required |
|
404 | Not found |
|
429 | Rate limited |
|
503 | Unavailable |
|
Coverage tables
Generated from the same catalog the API serves.
Sports & leagues
| Key | Name | Group | League |
|---|---|---|---|
nfl | NFL | american_football | yes |
ncaaf | NCAAF | american_football | yes |
nba | NBA | basketball | yes |
ncaab | NCAAB | basketball | yes |
wnba | WNBA | basketball | yes |
mlb | MLB | baseball | yes |
nhl | NHL | hockey | yes |
mls | MLS | soccer | yes |
epl | Premier League | soccer | yes |
la_liga | La Liga | soccer | yes |
serie_a | Serie A | soccer | yes |
bundesliga | Bundesliga | soccer | yes |
ligue_1 | Ligue 1 | soccer | yes |
ucl | UEFA Champions League | soccer | yes |
europa_league | UEFA Europa League | soccer | yes |
world_cup | FIFA World Cup | soccer | yes |
soccer_intl | Soccer (other / international) | soccer | — |
ufc | UFC | combat | yes |
tennis | Tennis (ATP / WTA) | tennis | — |
boxing | Boxing | combat | — |
Books (currently live)
This list is the customer-visible live set from the published snapshot index. It updates when books are promoted or paused in admin — no docs deploy required. Source of truth: GET /api/v1/books / GET /api/v1/catalog.
| Key | Name | Domain | Region |
|---|---|---|---|
betmgm | BetMGM | betmgm.com | us |
betonline | BetOnline | betonline.ag | offshore |
betrivers | BetRivers | betrivers.com | us |
draftkings | DraftKings | draftkings.com | us |
fanduel | FanDuel | fanduel.com | us |
novig | Novig | novig.us | exchange |
Markets
| Key | Name | Kind | Status | Queryable |
|---|---|---|---|---|
h2h | Moneyline | game_line | available | yes |
spreads | Spread | game_line | available | yes |
totals | Total | game_line | available | yes |
team_totals | Team total | team_prop | planned | no |
period_h2h | Period moneyline | period | planned | no |
period_spreads | Period spread | period | planned | no |
period_totals | Period total | period | planned | no |
player_pass_yds | Player passing yards | player_prop | planned | no |
player_pass_tds | Player passing touchdowns | player_prop | planned | no |
player_rush_yds | Player rushing yards | player_prop | planned | no |
player_receiving_yds | Player receiving yards | player_prop | planned | no |
player_receptions | Player receptions | player_prop | planned | no |
player_points | Player points | player_prop | planned | no |
player_rebounds | Player rebounds | player_prop | planned | no |
player_assists | Player assists | player_prop | planned | no |
player_threes | Player threes | player_prop | planned | no |
player_strikeouts | Player strikeouts | player_prop | planned | no |
player_hits | Player hits | player_prop | planned | no |
player_shots | Player shots on goal | player_prop | planned | no |
player_anytime_td | Anytime touchdown scorer | player_prop | planned | no |
live | Live / in-play | special | unsupported | no |
player_pass_ydsplayer_pass_tdsplayer_rush_ydsplayer_receiving_ydsplayer_receptionsplayer_pointsplayer_reboundsplayer_assistsplayer_threesplayer_strikeoutsplayer_hitsplayer_shotsplayer_anytime_tdPlanned player-prop keys are documentation-only until the product expands.
Code examples
import os
import requests
API_KEY = os.environ["ODDSVAULT_API_KEY"] # ov_live_...
BASE = "https://www.oddsvault.app"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
def get(path: str, **params):
r = requests.get(f"{BASE}{path}", params=params or None, headers=HEADERS, timeout=30)
r.raise_for_status()
return r.json()
# 1) Is data fresh?
status = get("/api/v1/status")
fresh = [s["sport"] for s in status["sports"] if s["fresh"]]
print("fresh sports:", fresh)
# 2) Discover coverage
catalog = get("/api/v1/catalog")
soccer = get("/api/v1/sports", group="soccer")
props = get("/api/v1/markets", kind="player_prop") # planned only
# 3) List games cheaply, then pull one event
board = get("/api/v1/events", sport="nba", limit=25)
if board["events"]:
eid = board["events"][0]["event_id"]
detail = get(f"/api/v1/events/{eid}", sport="nba", include_best="true")
print(detail["summary"]["participants"], detail.get("best_lines", [])[:1])
# 4) Full sport snapshot (or filter to one event / window)
data = get(
"/api/v1/odds",
sport="nfl",
books="draftkings,fanduel,pinnacle",
markets="h2h,spreads,totals",
)
# 5) Convert prices
print(get("/api/v1/convert", american=-110))
print(get("/api/v1/convert", home=-115, away=-105))
const API_KEY = process.env.ODDSVAULT_API_KEY!;
const BASE = "https://www.oddsvault.app";
async function ovGet<T>(path: string, params?: Record<string, string>): Promise<T> {
const url = new URL(path, BASE);
if (params) for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
const res = await fetch(url, {
headers: { Authorization: `Bearer ${API_KEY}` },
cache: "no-store",
});
if (!res.ok) throw new Error(`Odds Vault ${res.status}: ${await res.text()}`);
return res.json() as Promise<T>;
}
const status = await ovGet<{ summary: { sports_fresh: number } }>("/api/v1/status");
const events = await ovGet<{ events: Array<{ event_id: string }> }>("/api/v1/events", {
sport: "mlb",
limit: "20",
});
if (events.events[0]) {
const detail = await ovGet<{ best_lines?: unknown }>(
`/api/v1/events/${events.events[0].event_id}`,
{ sport: "mlb", include_best: "true" },
);
console.log(status.summary.sports_fresh, detail.best_lines);
}
const converted = await ovGet("/api/v1/convert", { american: "-110" });
const openapi = await fetch("https://www.oddsvault.app/api/v1/openapi").then((r) => r.json());
console.log(converted, openapi.info.version);
Claude Code handoff
Full contract: endpoint map, recommended flow, params, planned props, errors, checklist. Copy → paste into Claude Code.
# Odds Vault API — agent handoff
Use this contract when building against Odds Vault **v1**.
## Product boundaries
- Pregame full-game markets only today: moneyline / spread / total (`h2h`, `spreads`, `totals`).
- Responses are private R2 **current** snapshots (rolling per-book refresh; default 60s target, ≤180s source age). Supabase is the control plane only. Requests never scrape books and never fall back to Supabase snapshot tables.
- **Standard and Fresh read the same R2 objects.** Fresh only raises request allowance; it does not buy a separate feed or younger source data.
- **Live books** (customer-visible) are published on the snapshot index and listed by `GET /api/v1/books` / catalog — currently: betmgm, betonline, betrivers, draftkings, fanduel, novig. This set changes without a code deploy when admin promotes/pauses books.
- Account-owned keys from the dashboard only (`ov_live_…`). Entitlement required (`has_paid`, including active trial).
- Rate limits (dual fixed windows, always both): Standard 60/min + 360/hr; Fresh 300/min + 900/hr. Unlimited = no daily/monthly quota.
- Live odds out of scope. Player props / team totals / periods are cataloged as planned, not queryable on /odds.
- Internal risk scores and quarantined rows are never returned.
## Auth
- Header: `Authorization: Bearer ov_live_<prefix>_<secret>`
- Env var: `ODDSVAULT_API_KEY`
- All routes require a **paid/trial-entitled** account key **except** `GET /api/v1/openapi` (public OpenAPI JSON).
- Signup alone is not enough: subscribe (or start trial) from the dashboard, then mint a key.
- Successful paid responses include six headers: `X-RateLimit-Minute-Limit|Remaining|Reset` and `X-RateLimit-Hour-Limit|Remaining|Reset`.
## Endpoint map
- GET /api/v1/catalog — Full catalog: Sports, books, markets (incl. planned props), product notes, counts.
- GET /api/v1/sports — Sports & leagues: Keys for sport=. Optional group=soccer|basketball|…
- GET /api/v1/books — Sportsbooks: Live books from the R2 index (keys for books=). Soft-empty if index unavailable.
- GET /api/v1/markets — Markets & prop types: status / kind / queryable filters. Planned props included.
- GET /api/v1/status — Freshness status: R2 index board: per-sport fresh/has_snapshot, ages, books. 503 if index missing; ok:false when all stale.
- GET /api/v1/events — Event index: Lightweight board from R2: teams, commence, market/book counts — no quotes.
- GET /api/v1/events/{eventId} — Event detail: Full multi-book markets for one event (R2). include_best=true optional.
- GET /api/v1/odds — Odds snapshot: Full multi-book pregame R2 payload for a sport (+ event_id / commence filters).
- GET /api/v1/convert — Odds convert: American ↔ decimal ↔ implied, or two-way no-vig (home+away).
- GET /api/v1/openapi — OpenAPI 3.1: Machine-readable schema for agents/codegen. No auth required.
OpenAPI (no auth): https://www.oddsvault.app/api/v1/openapi
## Recommended client flow
1. `GET /api/v1/status` — skip sports that are not `fresh`. (503 if the R2 index itself is missing.)
2. `GET /api/v1/catalog` (or /sports, /books, /markets) — populate validators / UI. Prefer live books from API over hardcoded lists.
3. `GET /api/v1/events?sport=` — cheap board for pickers.
4. `GET /api/v1/events/{eventId}?include_best=true` or `GET /api/v1/odds?sport=&event_id=` — full quotes.
5. `GET /api/v1/convert` — American/decimal/implied or two-way no-vig.
## Odds: GET https://www.oddsvault.app/api/v1/odds
| Param | Required | Notes |
| --- | --- | --- |
| sport | yes | nfl, ncaaf, nba, ncaab, wnba, mlb, nhl, mls, epl, la_liga, serie_a, bundesliga, ligue_1, ucl, europa_league, world_cup, soccer_intl, ufc, tennis, boxing |
| books | no | live set e.g. betmgm, betonline, betrivers, draftkings, fanduel, novig |
| markets | no | queryable only: h2h, spreads, totals |
| event_id | no | single event |
| commence_after | no | ISO-8601 |
| commence_before | no | ISO-8601 |
Available market aliases: h2h→moneyline, spreads→spread, totals→total
Planned player props (not on /odds): player_pass_yds, player_pass_tds, player_rush_yds, player_receiving_yds, player_receptions, player_points, player_rebounds, player_assists, player_threes, player_strikeouts, player_hits, player_shots, player_anytime_td
Odds headers: `ETag`, `X-Odds-Snapshot-Version`, `X-Odds-Age-Seconds`, plus rate-limit headers. Send `If-None-Match` for 304.
## Events
- `GET /api/v1/events` — summaries with market_count, book_count, books[], market_families[], snapshot_version, age_seconds. Params: sport?, books?, markets?, commence_after?, commence_before?, limit? (default 100, max 500). Response includes limit + max_source_age_seconds.
- `GET /api/v1/events/{eventId}` — full event + optional best_lines. Params: sport?, books?, markets?, include_best=true.
## Status
- `GET /api/v1/status` → ok, refresh_target_seconds, max_source_age_seconds, checked_at, summary (sports_total / sports_with_snapshot / sports_fresh / events_fresh), sports[] with fresh, has_snapshot, version, age_seconds, books[], source_freshness[].
- Valid index with every sport stale → **200** `{ ok: false }`. Missing/invalid index → **503**.
## Convert
- Single: `?american=-110` OR `?decimal=1.91` OR `?implied=0.5238`
- Two-way no-vig: `?home=-110&away=-105` (American)
## Errors
400 bad input · 401 auth · 403 subscription_required · 404 event missing · 429 rate limit (minute_rate_limit_exceeded | hourly_rate_limit_exceeded | unauthorized_rate_limit_exceeded) · 503 R2/index/freshness/rate-limit provider
Unauthorized abuse windows: 60/min + 360/hr before generic 401 path.
## Minimal curls
```bash
curl 'https://www.oddsvault.app/api/v1/status' -H 'Authorization: Bearer ov_live_YOUR_KEY'
curl 'https://www.oddsvault.app/api/v1/catalog' -H 'Authorization: Bearer ov_live_YOUR_KEY'
curl 'https://www.oddsvault.app/api/v1/events?sport=nba' -H 'Authorization: Bearer ov_live_YOUR_KEY'
curl 'https://www.oddsvault.app/api/v1/odds?sport=nfl&markets=h2h,spreads,totals&books=draftkings,pinnacle' \
-H 'Authorization: Bearer ov_live_YOUR_KEY'
curl 'https://www.oddsvault.app/api/v1/convert?american=-110' -H 'Authorization: Bearer ov_live_YOUR_KEY'
curl 'https://www.oddsvault.app/api/v1/openapi'
```
## Implementation checklist
1. Read ODDSVAULT_API_KEY from env (account key from dashboard after entitled subscription/trial).
2. Prefer /status + /catalog + /books over hardcoded sports/books lists.
3. Use /events for boards; /events/{id} or /odds?event_id= for detail.
4. Never send planned market keys to /odds (400).
5. Treat 503 as retry later (missing R2, stale sources, or rate-limit provider). A fresh empty board is 200 with event_count 0.
6. Handle 403 subscription_required by sending the user to checkout/dashboard — not by retrying.
7. Use price_decimal for EV; american for display.
8. Fetch OpenAPI when generating clients.
9. Do not assume Fresh has newer odds than Standard — same snapshot, higher poll allowance only.
## Sport keys
nfl (NFL), ncaaf (NCAAF), nba (NBA), ncaab (NCAAB), wnba (WNBA), mlb (MLB), nhl (NHL), mls (MLS), epl (Premier League), la_liga (La Liga), serie_a (Serie A), bundesliga (Bundesliga), ligue_1 (Ligue 1), ucl (UEFA Champions League), europa_league (UEFA Europa League), world_cup (FIFA World Cup), soccer_intl (Soccer (other / international)), ufc (UFC), tennis (Tennis (ATP / WTA)), boxing (Boxing)
Tip: “Fetch OpenAPI + /status, then build a CLI that lists fresh NBA events and prints best moneylines.”