Here we will discuss the most economical ways to call the API in order to retrieve a maximum amount of information with a minimum number of calls and also avoid making unnecessary calls to the API that will return empty responses.
It is important to take into account to save calls to the API, that all competitions do not necessarily benefit from all the features of the API.
For this in the endpoint leagues
, there is a coverage
field that tells you wich features are covered for each league.
let’s test and call the endpoint leagues without any parameters, this will return the complete list of leagues in the API.
fetch("https://api-football-v1.p.rapidapi.com/v2/leagues", {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
And we get
{
"api": {
"results": 2651,
"leagues": [
{
"league_id": 524,
"name": "Premier League",
"type": "League",
"country": "England",
"country_code": "GB",
"season": 2019,
"season_start": "2019-08-09",
"season_end": "2020-07-26",
"logo": "https://media.api-sports.io/football/leagues/39.png",
"flag": "https://media.api-sports.io/flags/gb.svg",
"standings": 1,
"is_current": 1,
"coverage": {
"standings": true,
"fixtures": {
"events": true,
"lineups": true,
"statistics": true,
"players_statistics": true
},
"players": true,
"topScorers": true,
"predictions": true,
"odds": true
}
},
{...}
]
}
}
We only keep one league for example. So we can see from the field coverage that this competition benefits from all the API features.
It is recommended to save this information on your side so that you don’t have to call the endpoint leagues
again. Ideally, this data should be updated once a week.
It would be interesting in all your calls to the API to take into account the coverage
of a league before calling the API, this will avoid empty responses and unnecessary calls.
It must also be kept in mind that this endpoint returns the data coverage present in the API, so a competition that hasn’t started yet will have all the fields in FALSE
, that’s why you have to call this endpoint at least once a week. Particular case concerning the odds, we only keep a history of 3 months, so after this period and the competition finished the field will automatically be at FALSE
.
The API also contains a lot of data that is not updated regularly and that you can save on your side and update once or twice a month. Here are a few examples:
- teams
- coaches
- trophies
- sidelined
- transfers
FIXTURES AND LIVESCORE
Let’s now take a look at the livescore which is a central part of the API and study the best way to retrieve all the data from a fixture in a minimum of API calls.
For this we want to get:
- fixtures information (Teams, scores etc…)
- events
- lineups
- fixtures statistics
- players statistics
As a reminder the updates of these data are:
- fixtures: every 15 seconds
- events: every 15 seconds
- lineups: available between 20 and 40 minutes before the fixture
- fixtures statistics: every minute
- players statistics : every minute
To update fixtures and events, a single call to the fixtures/live
endpoint is enough as it returns all current fixtures and their events
, this avoids calling the endpoint events for each fixtures and so we’re saving on API calls.
The fixtures/live
endpoint will return a list of all current fixtures whose statusShort
will be one of these 1H, HT, 2H, ET, P, BT
you can check the documentation for more information about fixtures status.
Let’s try
fetch("https://api-football-v1.p.rapidapi.com/v2/fixtures/live", {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
And we get
{
"api": {
"results": 1,
"fixtures": [
{
"fixture_id": 245427,
"league_id": 979,
"league": {
"name": "A-League",
"country": "Australia",
"logo": "https://media.api-sports.io/football/leagues/188.png",
"flag": "https://media.api-sports.io/flags/au.svg"
},
"event_date": "2020-07-22T09:30:00+00:00",
"event_timestamp": 1595410200,
"firstHalfStart": 1595410200,
"secondHalfStart": null,
"round": "Regular Season - 27",
"status": "First Half",
"statusShort": "1H",
"elapsed": 44,
"venue": "HBF Park",
"referee": "Alexander King, Australia",
"homeTeam": {
"team_id": 940,
"team_name": "Perth Glory",
"logo": "https://media.api-sports.io/football/teams/940.png"
},
"awayTeam": {
"team_id": 942,
"team_name": "Wellington Phoenix",
"logo": "https://media.api-sports.io/football/teams/942.png"
},
"goalsHomeTeam": 0,
"goalsAwayTeam": 1,
"score": {
"halftime": "0-1",
"fulltime": null,
"extratime": null,
"penalty": null
},
"events": [
{
"elapsed": 32,
"elapsed_plus": null,
"team_id": 942,
"teamName": "Wellington Phoenix",
"player_id": 19400,
"player": "G. Hooper",
"assist_id": 94322,
"assist": "C. Mccowatt",
"type": "Goal",
"detail": "Normal Goal",
"comments": null
}
]
}
]
}
}
There’s a fixture in progress right now and we also notice that the events
field contains data that we will be able to save or display on our side. If there had been several fixtures, we would have gotten them all back and their events
.
We don’t want to stop there and also want to get the lineups, the fixtures statistics and the players statistics back.
As the lineups are updated only once and do not change during the fixture, once you have retrieved the data it is no longer necessary to update them.
The fixtures and players statistics are updated every minute, so it’s a good idea to update them as regularly as possible but only if the fixture is in progress, it’s not necessary to update them when the fixture status is at half time for example.
There is an endpoint for each of the data we want to retrieve:
- lineups/{fixture_id}
- statistics/fixture/{fixture_id}
- players/fixtures/{fixture_id}
But this will make us 3 calls to the API per fixture, to save calls we’ll go through the endpoint fixtures/id/{fixture_id}
which contains all the information of the 3 previous endpoints.
To retrieve them, we create a function that will do the necessary, let’s call it getFixtureById()
.
We will also create a condition to retrieve only the information from fixtures that are in progress and whose status are 1H, 2H, ET
.
Let’s try
var statusForStats = ['1H', '2H', 'ET'] // Only fixtures in progress
fetch("https://api-football-v1.p.rapidapi.com/v2/fixtures/live", {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
.then((response) => response.json())
.then(function(response) {
response.api.fixtures.forEach(function(fixture) {
// We check that the event field is not null and that it is a table containing at least one element, otherwise it is not necessary to process empty data
if (fixture.events != null && Array.isArray(fixture.events) && fixture.events.length > 0) {
// Saving or display events
}
// We check if the fixture has a status among those we are looking for to update all data
if (statusForStats.indexOf(fixture.statusShort) != '-1') {
// If yes we call our function to get all data
getFixtureById(fixture.fixture_id)
}
});
})
function getFixtureById(fixture_id) {
fetch("https://api-football-v1.p.rapidapi.com/v2/fixtures/id/"+fixture_id, {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
.then((response) => response.json())
.then(function(response) {
// We can now check if the data is present for each of the fields and save or display them
// The lineups and the fixtures statistics being objects we adapt our check to know if the field contains data
if (response.api.fixtures[0].lineups != null && Object.keys(response.api.fixtures[0].lineups).length !== 0) {
// Saving or display lineups
}
if (response.api.fixtures[0].statistics != null && Object.keys(response.api.fixtures[0].statistics).length !== 0) {
// Saving or display statistics
}
if (response.api.fixtures[0].players != null && Array.isArray(response.api.fixtures[0].players) && response.api.fixtures[0].players.length > 0) {
// Saving or display players statistics
}
})
}
The result of our getFixtureById()
function will be like this (We’ve removed data to make it more readable).
{
"api": {
"results": 1,
"fixtures": [
{
"fixture_id": 245427,
"league_id": 979,
"league": {
"name": "A-League",
"country": "Australia",
"logo": "https://media.api-sports.io/football/leagues/188.png",
"flag": "https://media.api-sports.io/flags/au.svg"
},
"event_date": "2020-07-22T09:30:00+00:00",
"event_timestamp": 1595410200,
"firstHalfStart": 1595410200,
"secondHalfStart": null,
"round": "Regular Season - 27",
"status": "First Half",
"statusShort": "1H",
"elapsed": 44,
"venue": "HBF Park",
"referee": "Alexander King, Australia",
"homeTeam": {
"team_id": 940,
"team_name": "Perth Glory",
"logo": "https://media.api-sports.io/football/teams/940.png"
},
"awayTeam": {
"team_id": 942,
"team_name": "Wellington Phoenix",
"logo": "https://media.api-sports.io/football/teams/942.png"
},
"goalsHomeTeam": 0,
"goalsAwayTeam": 1,
"score": {
"halftime": "0-1",
"fulltime": null,
"extratime": null,
"penalty": null
},
"events": [
{
"elapsed": 32,
"elapsed_plus": null,
"team_id": 942,
"teamName": "Wellington Phoenix",
"player_id": 19400,
"player": "G. Hooper",
"assist_id": 94322,
"assist": "C. Mccowatt",
"type": "Goal",
"detail": "Normal Goal",
"comments": null
}
],
"lineups": {
"Perth Glory": {
"coach": "T. Popović",
"coach_id": 151,
"formation": "3-5-2",
"startXI": [
{
"team_id": 940,
"player_id": 6795,
"player": "Liam Reddy",
"number": 33,
"pos": "G"
},
{
"team_id": 940,
"player_id": 44727,
"player": "Osama Malik",
"number": 13,
"pos": "D"
},
{...}
],
"substitutes": [
{
"team_id": 940,
"player_id": 6871,
"player": "Daniel Margush",
"number": 50,
"pos": "G"
},
{...}
]
},
"Wellington Phoenix": {
"coach": "U. Talay",
"coach_id": 1144,
"formation": "4-4-2",
"startXI": [
{
"team_id": 942,
"player_id": 19257,
"player": "Stefan Marinovic",
"number": 1,
"pos": "G"
},
{
"team_id": 942,
"player_id": 6925,
"player": "Louis Fenton",
"number": 16,
"pos": "D"
},
{...}
],
"substitutes": [
{
"team_id": 942,
"player_id": 94350,
"player": "Zac Jones",
"number": 30,
"pos": "G"
},
{...}
]
}
},
"statistics": {
"Shots on Goal": {
"home": "1",
"away": "1"
},
"Shots off Goal": {
"home": "0",
"away": "0"
},
"Total Shots": {
"home": "1",
"away": "2"
},
"Blocked Shots": {
"home": "0",
"away": "1"
},
"Shots insidebox": {
"home": "0",
"away": "1"
},
"Shots outsidebox": {
"home": "1",
"away": "1"
},
"Fouls": {
"home": "5",
"away": "2"
},
"Corner Kicks": {
"home": "0",
"away": "2"
},
"Offsides": {
"home": "0",
"away": "1"
},
"Ball Possession": {
"home": "59%",
"away": "41%"
},
"Yellow Cards": {
"home": null,
"away": null
},
"Red Cards": {
"home": null,
"away": null
},
"Goalkeeper Saves": {
"home": "0",
"away": "1"
},
"Total passes": {
"home": "295",
"away": "198"
},
"Passes accurate": {
"home": "259",
"away": "166"
},
"Passes %": {
"home": "88%",
"away": "84%"
}
},
"players": [
{
"event_id": 245427,
"updateAt": 1595413143,
"player_id": 6795,
"player_name": "Liam Reddy",
"team_id": 940,
"team_name": "Perth Glory",
"number": 33,
"position": "G",
"rating": "6.3",
"minutes_played": 37,
"captain": "False",
"substitute": "False",
"offsides": null,
"shots": {
"total": 0,
"on": 0
},
"goals": {
"total": 0,
"conceded": 1,
"assists": 0,
"saves": 0
},
"passes": {
"total": 3,
"key": 0,
"accuracy": 37
},
"tackles": {
"total": 0,
"blocks": 0,
"interceptions": 0
},
"duels": {
"total": 0,
"won": 0
},
"dribbles": {
"attempts": 0,
"success": 0,
"past": 0
},
"fouls": {
"drawn": 0,
"committed": 0
},
"cards": {
"yellow": 0,
"red": 0
},
"penalty": {
"won": 0,
"commited": 0,
"success": 0,
"missed": 0,
"saved": 0
}
},
{...}
]
}
]
}
}
Since there is only one match in progress, in 2 calls to the API we will retrieve all the following data:
- fixture information
- events
- lineups
- fixture statistics
- players statistics
Of course this script can be improved by adding checks if the league contains the data we are looking for, otherwise we will avoid calling the API. And concerning the lineups we can create a specific function that will check if the data is already present in your database to avoid unnecessary updates.
STANDINGS
Now that we’ve managed to save calls on fixtures, let’s see what we can put in place for standings.
As a reminder, standings are updated every hour for competitions with at least one fixture taking place on the same day.
So the ideal is to set up a script that collects all the leagues with at least one fixture taking place today and for each of the corresponding leagues call the endpoint leagueTable
.
It would also be a good idea to call the endpoint leagueTable
only for leagues that have this feature, we already saw this at the beginning of our tutorial, if the coverage.standings
field is equal to FALSE
it is not necessary to call the API for this competition.
To start we will get the list of fixtures that take place today using the endpoint fixtures/date/{YYYYYY-MM-DD}
.
It is also possible to set the timezone of your choice according to your country. Check the timezone documentation for that.
Let’s do this
var leagueIdsForStandings = [] // array containing the list of league_id with at least one match on that day
fetch("https://api-football-v1.p.rapidapi.com/v2/fixtures/date/2020-07-22", {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
.then((response) => response.json())
.then(function(response) {
response.api.fixtures.forEach(function(fixture) {
// we fill in our array only if the league_id is not present in leagueIdsForStandings[]
// This saves us from calling the leaguetable endpoint several times for a competition if it has several fixtures that day
if (leagueIdsForStandings.indexOf(fixture.league_id) == '-1') {
leagueIdsForStandings.push(fixture.league_id)
}
});
console.log(leagueIdsForStandings)
})
Will return
0: 1264
1: 1390
2: 979
3: 1357
4: 1380
5: 960
6: 2109
7: 1370
8: 1315
9: 1484
10: 1338
11: 511
12: 609
13: 1344
14: 2278
15: 515
16: 576
17: 2648
18: 524
19: 1329
20: 962
21: 891
22: 1365
23: 1361
24: 1331
25: 1347
26: 1352
27: 776
28: 779
29: 577
30: 565
31: 968
32: 1345
33: 1356
34: 2251
35: 1328
36: 2257
37: 2248
We can now create a function that will get all of the standings for these league_id.
Let’s call it getStandingsByLeague()
var leagueIdsForStandings = [] // array containing the list of league_id with at least one match on that day
fetch("https://api-football-v1.p.rapidapi.com/v2/fixtures/date/2020-07-22", {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
.then((response) => response.json())
.then(function(response) {
response.api.fixtures.forEach(function(fixture) {
// we fill in our array only if the league_id is not present in leagueIdsForStandings[]
// This saves us from calling the leaguetable endpoint several times for a competition if it has several fixtures that day
if (leagueIdsForStandings.indexOf(fixture.league_id) == '-1') {
leagueIdsForStandings.push(fixture.league_id)
}
});
leagueIdsForStandings.forEach(function(league_id) {
getStandingsByLeague(league_id)
})
})
function getStandingsByLeague(league_id) {
fetch("https://api-football-v1.p.rapidapi.com/v2/leagueTable/"+league_id, {
"method": "GET",
"headers": {
"x-rapidapi-host": "api-football-v1.p.rapidapi.com",
"x-rapidapi-key": "YourApiKeyHere"
}
})
.then((response) => response.json())
.then(function(response) {
console.log(response)
})
}
And this is what we get for each of the calls to the endpoint leagueTable
.
{
"api": {
"results": 1,
"standings": [
[
{
"rank": 1,
"team_id": 40,
"teamName": "Liverpool",
"logo": "https://media.api-sports.io/football/teams/40.png",
"group": "Premier League",
"forme": "LDWWL",
"status": "same",
"description": "Promotion - Champions League (Group Stage)",
"all": {
"matchsPlayed": 36,
"win": 30,
"draw": 3,
"lose": 3,
"goalsFor": 77,
"goalsAgainst": 29
},
"home": {
"matchsPlayed": 18,
"win": 17,
"draw": 1,
"lose": 0,
"goalsFor": 47,
"goalsAgainst": 13
},
"away": {
"matchsPlayed": 18,
"win": 13,
"draw": 2,
"lose": 3,
"goalsFor": 30,
"goalsAgainst": 16
},
"goalsDiff": 48,
"points": 93,
"lastUpdate": "2020-07-22"
},
{...}
]
]
}
}