API Reference

One REST API for live and historical odds from 55+ bookmakers across 8 countries — opening lines, closing lines, alternate ladders, sharpness ratings and stats. JSON in, JSON out.

The base URL for all requests is:

https://api.vividodds.com/v1

Every response is JSON. All odds are decimal. Timestamps are ISO 8601 (UTC). Get a key from the dashboard and you can make your first call in under a minute.

Quickstart

# every book's price for one event, opening + closing
curl https://api.vividodds.com/v1/odds/OYIoIMcD?markets=total \
  -H "Authorization: Bearer vo_live_..."
const res = await fetch(
  "https://api.vividodds.com/v1/odds/OYIoIMcD?markets=total",
  { headers: { Authorization: "Bearer vo_live_..." } }
);
const data = await res.json();
import requests
r = requests.get(
    "https://api.vividodds.com/v1/odds/OYIoIMcD",
    params={"markets": "total"},
    headers={"Authorization": "Bearer vo_live_..."},
)
data = r.json()

Authentication

Authenticate every request with your secret key in the Authorization header as a bearer token. Keys are created and rotated in the dashboard. Never expose a secret key in client-side code.

Authorization: Bearer vo_live_9f3c...e21a
Test vs live. Keys prefixed vo_test_ return sample data and don't count against your quota. Swap to vo_live_ when you're ready.

Rate limits

Limits are per key and depend on your plan. Every response includes your current usage in the headers below. A 429 means you've hit the ceiling — back off and retry after the reset.

HeaderMeaning
X-RateLimit-LimitCalls allowed in the current window.
X-RateLimit-RemainingCalls left in the current window.
X-RateLimit-ResetUnix time when the window resets.

Pagination

List endpoints return up to 100 items per page. Pass cursor from a response's next field to fetch the following page; a null next means you've reached the end.

Events

GET/v1/events

List fixtures and finished events with results. Filter by sport, league, date range and status.

ParameterDescription
sport stringsoccer · tennis · basketball · hockey · baseball
league stringLeague id, e.g. epl. Optional.
date stringSingle day YYYY-MM-DD, or use from/to.
status stringscheduled · live · finished
{
  "data": [
    {
      "id": "OYIoIMcD",
      "sport": "tennis",
      "league": "atp",
      "home": "Sinner", "away": "Alcaraz",
      "startsAt": "2026-07-04T13:30:00Z",
      "status": "finished",
      "result": { "home": 2, "away": 1, "total": 27 }
    }
  ],
  "next": "c_8f2a..."
}

Odds

GET/v1/odds/{eventId}

Every book's current and opening price for an event, plus the no-vig consensus fair and the sharpest book. Ask for the markets and countries you want.

ParameterDescription
markets stringComma-separated: ml, total, spread, btts. Default all.
books stringFilter to specific books, e.g. bet365,winamax. Default all.
countries stringCountry markets, e.g. it,fr,es. Default all.
{
  "event": "Sinner v Alcaraz",
  "market": "total_games", "line": 22.5,
  "fair": { "over": 1.94, "under": 1.92 },
  "sharpest": "betfair",
  "books": [
    { "book": "bet365",  "country": "it", "open": 1.90, "now": 1.95 },
    { "book": "winamax", "country": "fr", "open": 1.85, "now": 2.02 }
  ]
}

Historical odds

GET/v1/historical

Bulk opening + closing odds across books, back to 2014. The dataset for backtesting a whole season or measuring CLV at scale. Paginated; also available as a one-time bulk export.

ParameterDescription
sport stringrequiredSport to pull.
from · to daterequiredDate range, YYYY-MM-DD.
market stringe.g. total. Optional.
book stringRestrict to one book. Optional.

Closing-line value

GET/v1/clv/{eventId}

The opening → closing journey for every selection, with the no-vig fair and the CLV of taking the open. Score any bet against the market's own closing consensus.

{
  "event": "Sinner v Alcaraz",
  "selections": [
    { "market": "total", "side": "over", "line": 22.5,
      "open": 1.99, "close": 1.94,
      "fairClose": 1.90, "clv": 0.026 }
  ]
}

Book sharpness

GET/v1/sharpness

Every book ranked by how well its closing prices predict real outcomes (Brier score), per sport and market. Follow the sharp lines; fade the soft ones.

ParameterDescription
sport stringrequiredSport to rate.
market stringe.g. total, ml. Default all.
{
  "sport": "soccer", "market": "total",
  "ranking": [
    { "rank": 1, "book": "betfair", "brier": 0.201, "n": 18402 },
    { "rank": 2, "book": "bet365",  "brier": 0.208, "n": 17233 }
  ]
}

Match stats

GET/v1/stats/{eventId}

Match and player statistics for an event — shots, cards, possession and sport-specific box scores — for modelling and settlement.

Errors

VividOdds uses conventional HTTP status codes. Errors return a JSON body with a machine-readable code and a human-readable message.

StatusMeaning
200OK.
400Bad request — a parameter is missing or malformed.
401Invalid or missing API key.
404No event or resource with that id.
429Rate limit reached — retry after the reset.
5xxSomething failed on our side. Safe to retry with backoff.
{
  "error": {
    "code": "invalid_parameter",
    "message": "Unknown sport 'crickett'. Try soccer, tennis, basketball."
  }
}

SDKs

Official libraries wrap auth, retries and pagination. Generated from our OpenAPI spec, so they're always in sync with the API.

npm install vividodds

import { VividOdds } from "vividodds";
const vo = new VividOdds("vo_live_...");
const odds = await vo.odds("OYIoIMcD", { markets: ["total"] });
pip install vividodds

from vividodds import VividOdds
vo = VividOdds("vo_live_...")
odds = vo.odds("OYIoIMcD", markets=["total"])
Ready to build? Grab a key and make your first call in a minute. Get started free →