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.
`leagues`.
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" } })
Return
{ "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" } })
Return :
{ "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" }, {...} ] ] } }