Football Betting API

Games. Odds. Outcomes. One API.

Pre-match odds and settlement outcomes for football across 2,000+ leagues. Games, odds from multiple bookmakers, and machine-readable outcomes for bet settlement. You build the sportsbook.

Base URL https://api.sportlogic.io/api/v1
Auth X-API-Key: sl_YOUR_KEY
Leagues 2,000+
Markets 130+
Get API Key → Virtual Racing API Docs

Getting Started

Here's the typical workflow.

The complete workflow

  1. Get today's games - Find upcoming or live fixtures with GET /v1/games. Filter by league, date, or status.
  2. Get odds for a game - Fetch pre-match odds with GET /v1/games/{id}/odds. Each odd references a market_id from our markets table.
  3. After the game finishes, get outcomes - Call GET /v1/outcomes/{gameId} to get outcomes. Available ~5 minutes after the final whistle.
  4. Settle bets using outcomes - Match each outcome by market_id + option_name + option_value. The result field tells you won or lost.

First request

Replace YOUR_KEY with the API key from your dashboard. This fetches today's scheduled games.

curl -G https://api.sportlogic.io/api/v1/games \
  -H "X-API-Key: YOUR_KEY" \
  --data-urlencode "date_from=2026-04-05" \
  --data-urlencode "status=scheduled" \
  --data-urlencode "per_page=5"
Tip: All endpoints require the X-API-Key header. The GET /health endpoint is the only public route - no key needed.

Authentication

Every request requires a valid API key in the X-API-Key header.

Header

HeaderValueDescription
X-API-Keyyour_key_hereRequired on all endpoints. Obtained when you sign up.

Auth error codes

HTTPCodeMeaning
401MISSING_API_KEYNo X-API-Key header sent
401INVALID_API_KEYKey not found or deleted
403INACTIVE_API_KEYKey suspended

Rate Limits & Pricing

Limits are counted per API key per calendar day (UTC). Rate limits are enforced per minute.

PlanPriceDaily LimitRate Limit
Free$0500 requests/day10 req/min
Basic$19/mo7,500 requests/day60 req/min
Pro$39/mo75,000 requests/day300 req/min
Mega$59/mo150,000 requests/day600 req/min

Response headers

HeaderDescription
X-RateLimit-LimitYour daily request limit
X-RateLimit-RemainingRequests remaining today

On limit exceeded, the API returns HTTP 429 with error code RATE_LIMIT_EXCEEDED. Resets at UTC midnight.

Games

Football game fixtures with scores, status, and odds. The starting point for every workflow.

GET /api/v1/games

List games with optional filters. Returns cursor-paginated results ordered by start_time desc.

ParameterTypeRequiredDescription
league_idintegeroptionalFilter by league
statusstringoptionalscheduled live finished postponed cancelled
date_fromdateoptionalFormat YYYY-MM-DD
date_todateoptionalFormat YYYY-MM-DD. Must be ≥ date_from
is_livebooleanoptionalFilter to currently live games
per_pageintegeroptional1–100, default 50
cursorstringoptionalPagination cursor from previous response

Response fields

  • idGame ID, used consistently across odds and outcomes
  • statusscheduled · live · finished · postponed · cancelled
  • is_liveBoolean, true when game is actively in progress
  • start_timeISO 8601 UTC kick-off time
  • home_scoreHome goals scored, null if not started
  • away_scoreAway goals scored, null if not started
  • leagueEmbedded league object (id, name, country)
  • home_teamEmbedded team object (id, name, country)
  • away_teamEmbedded team object (id, name, country)
curl -G https://api.sportlogic.io/api/v1/games \
  -H "X-API-Key: YOUR_KEY" \
  --data-urlencode "date_from=2026-04-05" \
  --data-urlencode "status=finished" \
  --data-urlencode "per_page=25"
const res = await fetch(
  'https://api.sportlogic.io/api/v1/games?date_from=2026-04-05&status=finished',
  { headers: { 'X-API-Key': 'YOUR_KEY' } }
);
const { data, pagination } = await res.json();
import requests

r = requests.get(
    "https://api.sportlogic.io/api/v1/games",
    headers={"X-API-Key": "YOUR_KEY"},
    params={"date_from": "2026-04-05", "status": "finished"}
)
games = r.json()["data"]

Response
{ "success": true, "data": [ { "id": 41200, "league_id": 174, "home_team": { "id": 205, "name": "Mamelodi Sundowns", "logo_url": "https://media.api-sports.io/football/teams/2699.png" }, "away_team": { "id": 2712, "name": "Kaizer Chiefs", "logo_url": "https://media.api-sports.io/football/teams/2698.png" }, "start_time": "2026-04-03T17:00:00.000000Z", "status": "finished", "home_score": 4, "away_score": 1, "is_live": false } ], "meta": { "next_cursor": "eyJpZCI6NDEyMDB9" } }
GET /api/v1/games/{id}

Retrieve a single game by ID with full league and team details.

ParameterTypeRequiredDescription
idintegerrequiredGame ID (path parameter)
curl https://api.sportlogic.io/api/v1/games/41200 \
  -H "X-API-Key: YOUR_KEY"

Response
{ "success": true, "data": { "id": 41200, "league_id": 174, "home_team": { "id": 205, "name": "Mamelodi Sundowns", "logo_url": "https://media.api-sports.io/football/teams/2699.png" }, "away_team": { "id": 2712, "name": "Kaizer Chiefs", "logo_url": "https://media.api-sports.io/football/teams/2698.png" }, "start_time": "2026-04-03T17:00:00.000000Z", "status": "finished", "home_score": 4, "away_score": 1, "is_live": false } }

404 response
{ "success": false, "error": { "code": "RESOURCE_NOT_FOUND", "message": "Game not found" } }
GET /api/v1/games/{id}/odds

Pre-match odds for a game, grouped by market. Updated until kickoff. Each odd references a market_id that matches the outcomes endpoint.

ParameterTypeRequiredDescription
idintegerrequiredGame ID (path parameter)
market_idintegeroptionalFilter to a specific market
bookmaker_idintegeroptionalFilter to a specific bookmaker
The market_id on each odd is the same ID used in the outcomes endpoint.

Response fields

  • option_nameSelection label (e.g. "Home", "Draw", "Away", "Over", "Under")
  • option_valueSelection value (e.g. "2.5" for Over/Under lines), null for standard markets
  • oddsDecimal odds as string (e.g. "1.18"). Always parse with decimal-aware library.
  • is_suspendedBoolean, true if the bookmaker has temporarily suspended this line
  • market.keyMachine-readable market identifier (e.g. "match_winner", "goals_over_under")
curl https://api.sportlogic.io/api/v1/games/41200/odds \
  -H "X-API-Key: YOUR_KEY"

Response
{ "success": true, "data": [ { "id": 6631453, "game_id": 41200, "market_id": 1, "option_name": "Home", "option_value": null, "odds": "1.18", "is_suspended": false, "market": { "id": 1, "name": "Match Winner", "key": "match_winner", "category": "main" }, "bookmaker": { "id": 1, "name": "Bet365" } }, { "id": 6631454, "game_id": 41200, "market_id": 1, "option_name": "Draw", "option_value": null, "odds": "5.50", "is_suspended": false, "market": { "id": 1, "name": "Match Winner", "key": "match_winner", "category": "main" }, "bookmaker": { "id": 1, "name": "Bet365" } }, { "id": 6631455, "game_id": 41200, "market_id": 1, "option_name": "Away", "option_value": null, "odds": "13.00", "is_suspended": false, "market": { "id": 1, "name": "Match Winner", "key": "match_winner", "category": "main" }, "bookmaker": { "id": 1, "name": "Bet365" } } ] }

Odds

Pre-match odds query across multiple games. For odds on a single game, prefer GET /games/{id}/odds.

GET /api/v1/odds

Pre-match odds across multiple games with optional filters. Returns cursor-paginated results.

ParameterTypeRequiredDescription
game_idintegeroptionalFilter to a specific game
market_idintegeroptionalFilter to a specific market
bookmaker_idintegeroptionalFilter to a specific bookmaker
is_activebooleanoptionalFilter suspended vs active odds
per_pageintegeroptional1–100, default 50
cursorstringoptionalPagination cursor
Decimal format: All odds values are returned as strings in decimal format (e.g. "1.18"). Always parse with a decimal-aware library, never use floating point.
curl -G https://api.sportlogic.io/api/v1/odds \
  -H "X-API-Key: YOUR_KEY" \
  --data-urlencode "game_id=41200" \
  --data-urlencode "market_id=1"

Response
{ "success": true, "data": [ { "id": 6631453, "game_id": 41200, "market_id": 1, "option_name": "Home", "option_value": null, "odds": "1.18", "is_suspended": false, "market": { "id": 1, "name": "Match Winner", "key": "match_winner", "category": "main" }, "bookmaker": { "id": 1, "name": "Bet365" } } ], "meta": { "next_cursor": "eyJpZCI6NjYzMTQ1M30" } }

Outcomes

After a game finishes, outcomes are computed for every betting market. Results are typically available within 5 minutes.

GET /api/v1/outcomes/{gameId}

Get outcomes for a finished game. Use query params to filter by market, option, or result instead of fetching all outcomes.

ParameterTypeRequiredDescription
gameIdintegerrequiredGame ID (path parameter)
market_idintegeroptionalFilter by market ID
option_namestringoptionalFilter by option name (e.g. "Home", "Over")
option_valuestringoptionalFilter by option value (e.g. "2.5")
resultstringoptionalFilter by result: won or lost
Settlement
Each outcome maps a market option to its result. Match by market_id + option_name + option_value to settle bets.

Response fields

  • game_idThe game these outcomes belong to
  • calculated_atISO 8601 timestamp when outcomes were computed
  • outcomes[].market_idMarket ID, matches the market_id on odds
  • outcomes[].option_nameOption label (e.g. "Home", "Draw", "Over")
  • outcomes[].option_valueOption value if applicable (e.g. "2.5" for Over/Under), null otherwise
  • outcomes[].resultwon, lost, void, half_won, half_lost
  • outcomes[].marketEmbedded market object (id, name, key)

Result Values

won
The selection was correct. Pay out the bet.
lost
The selection was incorrect. Bet loses.
void
Market voided (e.g. player not in squad for goalscorer markets). Refund the stake.
half_won
Asian handicap: half the stake wins at full odds, half refunded.
half_lost
Asian handicap: half the stake loses, half refunded.

Settlement matching

How to settle: For each bet, find the outcome where market_id + option_name + option_value match the bet's selection. If result is "won", pay out. If "lost", the bet loses. That's it.

Error states

Error CodeMeaningretry_after
GAME_NOT_STARTEDGame hasn't kicked off yetKick-off time
GAME_IN_PROGRESSGame is currently livenull
GAME_CANCELLEDPostponed or abandonednull
OUTCOMES_PENDINGFinished, engine still calculating+5 minutes

curl https://api.sportlogic.io/api/v1/outcomes/41200 \
  -H "X-API-Key: sl_YOUR_KEY"


curl "https://api.sportlogic.io/api/v1/outcomes/41200?market_id=1&option_name=Home" \
  -H "X-API-Key: sl_YOUR_KEY"
const res = await fetch(
  'https://api.sportlogic.io/api/v1/outcomes/41200',
  { headers: { 'X-API-Key': 'YOUR_KEY' } }
);
if (!res.ok) throw new Error('Not ready yet');
const { data } = await res.json();


const bet = { market_id: 1, option_name: 'Home', option_value: null };
const match = data.outcomes.find(o =>
  o.market_id === bet.market_id &&
  o.option_name === bet.option_name &&
  o.option_value === bet.option_value
);
console.log(match.result); 
import requests

r = requests.get(
    "https://api.sportlogic.io/api/v1/outcomes/41200",
    headers={"X-API-Key": "YOUR_KEY"}
)
data = r.json()["data"]
for o in data["outcomes"]:
    print(o["market_id"], o["option_name"], "=>", o["result"])

Success response
{ "success": true, "data": { "game_id": 41200, "calculated_at": "2026-04-03T19:50:05+00:00", "outcomes": [ { "id": 4020572, "game_id": 41200, "market_id": 1, "option_name": "Home", "option_value": null, "result": "won", "market": { "id": 1, "name": "Match Winner", "key": "match_winner" } }, { "id": 4020571, "game_id": 41200, "market_id": 1, "option_name": "Draw", "option_value": null, "result": "lost", "market": { "id": 1, "name": "Match Winner", "key": "match_winner" } }, { "id": 4020570, "game_id": 41200, "market_id": 1, "option_name": "Away", "option_value": null, "result": "lost", "market": { "id": 1, "name": "Match Winner", "key": "match_winner" } } ] } }

Error - game in progress
{ "success": false, "error": { "code": "GAME_IN_PROGRESS", "message": "Game is currently in progress. Outcomes will be available after the game completes.", "errors": { "game_status": "Second Half", "game_completed": false, "retry_after": null } } }
GET /api/v1/outcomes

List all calculated outcomes across games. Filter by game or date range. Useful for batch settlement processing.

ParameterTypeRequiredDescription
game_idintegeroptionalFilter to a specific game
date_fromdateoptionalFilter by game start date from
date_todateoptionalFilter by game start date to
per_pageintegeroptional1–100, default 50
cursorstringoptionalCursor for next page
curl -G https://api.sportlogic.io/api/v1/outcomes \
  -H "X-API-Key: YOUR_KEY" \
  --data-urlencode "date_from=2026-04-05"
const res = await fetch(
  'https://api.sportlogic.io/api/v1/outcomes?date_from=2026-04-05',
  { headers: { 'X-API-Key': 'YOUR_KEY' } }
);
const { data } = await res.json();

Response
{ "success": true, "data": [ { "id": 4020572, "game_id": 41200, "market_id": 1, "option_name": "Home", "option_value": null, "result": "won", "market": { "id": 1, "name": "Match Winner", "key": "match_winner" } } ], "meta": { "next_cursor": "eyJpZCI6NDAyMDU3Mn0" } }

Reference

Lookup tables for leagues and markets. Use these IDs to filter games, odds, and outcomes.

GET /api/v1/leagues

List all active football leagues with country and tier.

  • idLeague ID - use to filter games
  • nameFull league name (e.g. "Bundesliga")
  • countryCountry name
  • logo_urlURL to league logo image
  • tierDivision tier (1 = top flight)
  • is_activeWhether the league is currently tracked
  • seasonCurrent season year (e.g. 2025)
curl https://api.sportlogic.io/api/v1/leagues \
  -H "X-API-Key: YOUR_KEY"

Response
{ "success": true, "data": [ { "id": 186, "name": "Bundesliga", "country": "Germany", "logo_url": "https://media.api-sports.io/football/leagues/78.png", "tier": 1, "is_active": true, "season": 2025 } ] }
GET /api/v1/markets

All betting markets available in the system. Use market_id to filter odds and match outcomes to bets.

FieldDescription
idMarket ID - referenced in odds and outcomes
nameDisplay name (e.g. "Match Winner", "Goals Over/Under")
keyMachine-readable identifier (e.g. "match_winner", "goals_over_under")
categoryMarket category grouping (e.g. "main", "goals")
The market_id is the common thread across odds and outcomes. When you fetch an odd with market_id: 1, the outcome for that same market will also have market_id: 1.
curl https://api.sportlogic.io/api/v1/markets \
  -H "X-API-Key: YOUR_KEY"

Response
{ "success": true, "data": [ { "id": 1, "name": "Match Winner", "key": "match_winner", "category": "main" }, { "id": 5, "name": "Goals Over/Under", "key": "goals_over_under", "category": "goals" } ] }

Errors

All error responses follow a consistent envelope. Machine-readable codes allow reliable error handling.

Error envelope

{
  "success": false,
  "error": {
    "code": "MACHINE_READABLE_CODE",
    "message": "Human-readable description",
    "errors": {  }
  }
}

Common error codes

HTTPCodeDescription
400VALIDATION_ERRORInvalid query parameters. See errors field.
401MISSING_API_KEYNo X-API-Key header
401INVALID_API_KEYKey not found or deleted
403INACTIVE_API_KEYKey suspended
404RESOURCE_NOT_FOUNDRequested resource doesn't exist
429RATE_LIMIT_EXCEEDEDDaily or per-minute limit reached. Resets at UTC midnight.
400GAME_NOT_STARTEDOutcomes requested for a future game
400GAME_IN_PROGRESSOutcomes requested while game is live
400OUTCOMES_PENDINGGame done but outcomes still calculating (~5 min)