Overview
In this tutorial, you'll learn how to build a live odds application using the API 4 Sports platform. We'll cover fetching live odds, pre-match odds, and implementing real-time price updates.
Available Odds Endpoints
API 4 Sports provides two main endpoints for odds data:
- /football/live-odds — Real-time odds for currently live matches
- /football/pre-match-odds — Pre-match odds for upcoming matches (max 5 days range)
- /tennis/live-odds — Live odds for tennis matches
- /tennis/pre-match-odds — Pre-match odds for tennis (max 5 days range)
Fetching Live Odds (Football)
Get real-time odds for all currently live football matches. You can filter by match_id, country_id, or league_id.
cURL Request
curl -X GET "https://api.api4sports.com/api/football/live-odds?league_id=207" \
-H "X-Api-Key: your_api_key_here"JavaScript Implementation
async function getLiveOdds(options = {}) {
const params = new URLSearchParams();
if (options.matchId) params.append('match_id', options.matchId);
if (options.leagueId) params.append('league_id', options.leagueId);
if (options.countryId) params.append('country_id', options.countryId);
const url = `https://api.api4sports.com/api/football/live-odds?${params}`;
const response = await fetch(url, {
headers: { 'X-Api-Key': process.env.API_KEY }
});
const data = await response.json();
return data.result;
}
// Usage - Get all live odds
const allLiveOdds = await getLiveOdds();
// Get odds for Premier League only
const premierLeagueOdds = await getLiveOdds({ leagueId: 207 });
// Get odds for a specific match
const matchOdds = await getLiveOdds({ matchId: 59409 });Python Implementation
import requests
def get_live_odds(match_id=None, league_id=None, country_id=None):
url = "https://api.api4sports.com/api/football/live-odds"
headers = {"X-Api-Key": "your_api_key_here"}
params = {}
if match_id:
params["match_id"] = match_id
if league_id:
params["league_id"] = league_id
if country_id:
params["country_id"] = country_id
response = requests.get(url, headers=headers, params=params)
return response.json().get("result", [])
# Usage
live_odds = get_live_odds(league_id=207)
for match in live_odds:
print(f"Match {match['match_id']}: {match['live_odds']}")Live Odds Response
{
"success": 1,
"result": [
{
"match_id": 59409,
"live_odds": {
"1X2": {
"Home": "1.85",
"Draw": "3.50",
"Away": "2.10"
},
"Over/Under": {
"Over 2.5": "1.90",
"Under 2.5": "1.90"
},
"Both Teams to Score": {
"Yes": "1.75",
"No": "2.05"
}
}
}
]
}Fetching Pre-Match Odds
Get odds for upcoming matches within a date range. Maximum 5 days range is supported.
cURL Request
curl -X GET "https://api.api4sports.com/api/football/pre-match-odds?from=2024-01-15&to=2024-01-20" \
-H "X-Api-Key: your_api_key_here"JavaScript Implementation
async function getPreMatchOdds(from, to, matchId = null) {
const params = new URLSearchParams({ from, to });
if (matchId) params.append('match_id', matchId);
const response = await fetch(
`https://api.api4sports.com/api/football/pre-match-odds?${params}`,
{ headers: { 'X-Api-Key': process.env.API_KEY } }
);
const data = await response.json();
return data.result;
}
// Usage
const odds = await getPreMatchOdds('2024-01-15', '2024-01-20');
// Iterate through matches
for (const [matchId, markets] of Object.entries(odds)) {
console.log(`Match ${matchId}:`);
for (const [market, selections] of Object.entries(markets)) {
console.log(` ${market}:`, selections);
}
}Pre-Match Odds Response
{
"success": 1,
"result": {
"1625280": {
"Match Winner": {
"Home": {
"bet365": "3.10",
"Marathon": "3.16",
"1xBet": "3.21"
},
"Draw": {
"bet365": "3.50",
"Marathon": "3.54"
},
"Away": {
"bet365": "2.25",
"Marathon": "2.31"
}
},
"Correct Score": {
"1:0": { "bet365": "8.00", "Marathon": "6.35" },
"2:1": { "bet365": "11.00", "Marathon": "8.50" },
"0:0": { "bet365": "10.00" }
},
"Over/Under 2.5": {
"Over": { "bet365": "1.90" },
"Under": { "bet365": "1.90" }
}
}
}
}Tennis Live Odds
Similar endpoints exist for tennis matches. Filter by match_id or tournament_id.
Tennis Live Odds Request
curl -X GET "https://api.api4sports.com/api/tennis/live-odds?tournament_id=11277" \
-H "X-Api-Key: your_api_key_here"Tennis Live Odds Response
{
"success": 1,
"result": [
{
"match_id": 12345,
"live_odds": {
"Match Winner": {
"1": { "bet365": "1.85", "Marathon": "1.90" },
"2": { "bet365": "2.10" }
},
"Set Winner": {
"1": { "bet365": "1.70" },
"2": { "bet365": "2.20" }
}
}
}
]
}Real-Time Odds Polling
For live odds, implement polling with short intervals (2-5 seconds) to capture price movements.
Odds Polling Class
class OddsPoller {
constructor(apiKey, sport = 'football') {
this.apiKey = apiKey;
this.sport = sport;
this.intervalId = null;
this.lastOdds = new Map();
}
async fetchLiveOdds(leagueId = null) {
const params = leagueId ? `?league_id=${leagueId}` : '';
const response = await fetch(
`https://api.api4sports.com/api/${this.sport}/live-odds${params}`,
{ headers: { 'X-Api-Key': this.apiKey } }
);
return (await response.json()).result;
}
detectChanges(newOdds) {
const changes = [];
for (const match of newOdds) {
const lastMatch = this.lastOdds.get(match.match_id);
if (!lastMatch) {
changes.push({ type: 'new', match });
continue;
}
// Compare odds and detect movements
const oddsChanges = this.compareOdds(lastMatch.live_odds, match.live_odds);
if (oddsChanges.length > 0) {
changes.push({ type: 'update', match, changes: oddsChanges });
}
}
// Update cache
newOdds.forEach(m => this.lastOdds.set(m.match_id, m));
return changes;
}
compareOdds(oldOdds, newOdds) {
const changes = [];
for (const [market, selections] of Object.entries(newOdds)) {
for (const [selection, price] of Object.entries(selections)) {
const oldPrice = oldOdds?.[market]?.[selection];
if (oldPrice && oldPrice !== price) {
changes.push({
market,
selection,
oldPrice: parseFloat(oldPrice),
newPrice: parseFloat(price),
direction: parseFloat(price) > parseFloat(oldPrice) ? 'up' : 'down'
});
}
}
}
return changes;
}
start(callback, intervalMs = 3000, leagueId = null) {
const poll = async () => {
const odds = await this.fetchLiveOdds(leagueId);
const changes = this.detectChanges(odds);
callback(odds, changes);
};
poll();
this.intervalId = setInterval(poll, intervalMs);
}
stop() {
if (this.intervalId) clearInterval(this.intervalId);
}
}
// Usage
const poller = new OddsPoller(process.env.API_KEY);
poller.start((odds, changes) => {
if (changes.length > 0) {
console.log('Price movements detected:', changes);
}
}, 3000, 207); // Poll Premier League every 3 secondsUI/UX Patterns for Odds Display
- Highlight price increases with green/up arrow
- Highlight price decreases with red/down arrow
- Show last update timestamp and connection status
- Animate price changes with subtle transitions
- Display bookmaker comparison in a grid layout
- Show line movement history for each selection
Architecture Best Practices
- Use short polling intervals (2-5s) for live odds
- Cache pre-match odds with longer TTL (1-5 minutes)
- Implement backpressure when API rate limits are approached
- Batch UI updates to reduce re-renders
- Use edge regions close to your users for lower latency
Error Handling
async function fetchOddsWithRetry(url, maxRetries = 3) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
const response = await fetch(url, {
headers: { 'X-Api-Key': process.env.API_KEY }
});
if (response.status === 429) {
// Rate limited - wait and retry
const waitTime = Math.pow(2, attempt) * 1000;
console.log(`Rate limited, waiting ${waitTime}ms...`);
await new Promise(r => setTimeout(r, waitTime));
continue;
}
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return await response.json();
} catch (error) {
if (attempt === maxRetries) throw error;
await new Promise(r => setTimeout(r, 1000 * attempt));
}
}
}Next Steps
Combine live odds with match data from the Live Scores API to build a complete betting companion app. See our Best Practices guide for production deployment tips.