Becoming a LoL Analyst: An Introduction to using the Riot API
An introductory guide on how to get League of Legends data from the Riot API, using Python.
A slightly different one for you guys this week — instead of showing you a statistical analysis, I’ll be teaching you how to do statistical analysis! We’re starting this series off with an introduction to the Riot API. You’ll find everything you need here to create your very first account analyser.
Before we start…
Do I need to know how to code?
No! Of course, it helps. But please, don’t let this put you off. When I first looked at the Riot API I didn’t know anything about coding and now I do it as a full time profession. You can learn the basics of coding AND the basics of the API at the same time. I won’t write a guide on learning Python since there’s millions out there, but hopefully the guide will give you the basics and you can go off and learn the rest yourself.
If you are new to Python, I recommend using Jupyter Notebooks — since it makes understanding what the code is doing a lot easier. You can download it here:
Install Jupyter Notebook through Anaconda: https://www.anaconda.com/products/distribution
I have also created this guide in the form of a notebook, which can be found here: https://github.com/JackWillz/The-Riot-API-Introduction-by-iTero-Gaming
A whole bunch of people with various background knowledge have helped me test this guide out, if you need some help you can join the iTero Discord and get advise, fix issues or find out more in the #riot-api-learners channel.
Getting Setup on the Riot API Developer Portal
To begin working with the Riot API, we must first set-up an account on the portal. Head over to: https://developer.riotgames.com/ and sign-in using your Riot account.
Once you’re in, head down to “REGENERATE YOUR API KEY” to get a temporary pass (lasts 24 hours). Remember, never share this key with anyone else and be careful where you store it! You can then copy it over to your Python interpreter, if you’re following the guide exactly it’ll be Jupyter Notebook.
Our First Riot API Call
It’s time to make our first call to the Riot API.
In order to do this, we need to find the API that we want to call, which is in the form of a URL. To find all the ones available to you, tab back into the developer portal and press “APIs”. On your left should be a list of all the available types of calls to be made — don’t be overwhelmed, they cover all Riot games and in reality you’ll only need to use a small number of them.
Scroll down until you see “SUMMONER-V4”, once you click it a list of URLs will appear. You’re looking for the one called “/lol/summoner/v4/summoners/by-name/{summonerName}”.
Go down the page until you reach the “PATH PARAMETERS”. Enter your desired Summoner Name, the region that Summoner is in and then ensure the “SELECT APP TO EXECUTE AGAINST” is set to “Development API Key”. Then hit EXECUTE REQUEST.
If everything’s gone well, you should get a “RESPONSE CODE” of 200. Copy the “REQUEST URL” into Python.
It should look something like this:
api_key = "YOUR_KEY"api_url = "https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/Jack%20J"
To manage our API requests we’ll be using a Python package called “requests”, which is usually preinstalled (if you get an error saying “No module named ‘requests’”, you’ll need to do some Googling on how to install packages).
Sending a request is easy:
import requests requests.get(api_url)
However, you’ll get an error code 401. You can check on the developer portal to find out what each code means. This one refers to “Unauthorized”. This is because we haven’t attached our API key to the request, and so the internet bouncers won’t let you in to get the data.
When dealing with APIs, we can tag on arguments at the end of the URL by adding “?” at the end. Like this:
api_url = api_url + '?api_key=' + api_key
Which makes the URL:
https://euw1.api.riotgames.com/lol/summoner/v4/summoners/by-name/Jack%20J?api_key=YOUR_API_KEY
Now, go back and resend the URL. This time you should receive the desired “200” response, which indicate it’s been successful. But where’s the data? Here’s how we extract it from the API response:
resp = requests.get(api_url)
player_info = resp.json()
The “player_info” object should look something like this:
{'id': 'gkjv-w3_gkWbFFmMZkrDj2JR-rrmv4Mj172gqRO6ycmY9FY',
'accountId': 'aD6DoTQb_RWYLAghp_mWwuwAWHOW6qRz6zgQsf2y5YIitQ',
'puuid': 'dIWQhEOVCY9QT93Jz0EX6YxQIfI3zsyL7tVNmyApKpPmvgNnq5zBu-VKtgGvt2bWzj-fQqd1mYVZMA',
'name': 'Jack J',
'profileIconId': 918,
'revisionDate': 1658870637000,
'summonerLevel': 346}
Getting Match Data
Sending our first API call was fairly straight-forward, except it doesn’t give us much information to work with. We’ll now look at getting historic match data for our account.
To do this, we must first work out what the API call needs to be. Again, go back to the portal and this time find “MATCH-V5” on the far left. Go to the option called: “/lol/match/v5/matches/by-puuid/{puuid}/ids”. The {puuid} indicates that we need to insert a puuid into the URL in order to use it. Luckily we got one from our first API call (go back up and check the “player_info” to find the puuid).
We can then copy and paste the puuid into the “PATH PARAMETERS” on the developer portal. Ensure the region and API key are also request, leave the remaining fields as they are and then EXECUTE REQUEST.
Once again, copy the REQUEST URL back into Python, it’ll look something like this:
api_url = "https://europe.api.riotgames.com/lol/match/v5/matches/by-puuid/dIWQhEOVCY9QT93Jz0EX6YxQIfI3zsyL7tVNmyApKpPmvgNnq5zBu-VKtgGvt2bWzj-fQqd1mYVZMA/ids?start=0&count=20"
Note, if you inspect the URL you’ll see there’s already two arguments at the end: “?start=0&count=20”. This means if we want to add a new argument (the API key), we’ll have to add “&” instead of “?”.
Once you’ve done that, send the URL off the same as before:
api_url = api_url + '&api_key=' + api_key
resp = requests.get(api_url)
match_ids = resp.json()
The match_ids will look something like this:
['EUW1_5987369961',
'EUW1_5987334405',
'EUW1_5987297136',
'EUW1_5985953834',
'EUW1_5985877576',
'EUW1_5984051808',
'EUW1_5983630065',
'EUW1_5983595510',
'EUW1_5982943551',
'EUW1_5982849305',
'EUW1_5982852241',
'EUW1_5982834160',
'EUW1_5982385567',
'EUW1_5982421620',
'EUW1_5981636284',
'EUW1_5981413357',
'EUW1_5981269044',
'EUW1_5981244521',
'EUW1_5980174276',
'EUW1_5980007970']
This contains a list of match IDs for the accounts most recent 20 games.
In order to find out more information about one of these matches we can use another API, which you can find in the portal called “/lol/match/v5/matches/{matchId}”.
In the portal, scroll down and copy one of the match IDs into the “matchId” input, ensuring the region and API key are the same as before. We can then copy the API URL and send it off through Python.
api_url = "https://europe.api.riotgames.com/lol/match/v5/matches/EUW1_5982385567"
# This one doesn't have any arguments (see, there's no "?" above) so we'll add it with the api key
api_url = api_url + '?api_key=' + api_key
resp = requests.get(api_url)
match_data = resp.json()
If all goes well you’ll find A LOT of fields in the “match_data” object. It contains all the information about the match such as game length, start time and which puuids were in the game. It also contains information about each player and their performance, such as KDA or their champions name. Let’s take a look at one of the players KDA:
# To save time, we'll assign a variable for the first player
player_data = match_data['info']['participants'][0]k = player_data['kills']
d = player_data['deaths']
a = player_data['assists']
print("Kills:", k)
print("Deaths:", d)
print("Assists:", a)
print("KDA:", (k + a) / d)
However, we don’t want the information about any random player in that game! We want to find out how the summoner we searched for originally had performed. In order to do this, we need to find in which order they appear.
To do this, grab a list of all the participant puuids and use the “index” function to find out where they are.
# A list of all the participants puuids
participants = match_data['metadata']['participants']
# Now, find where in the data our players puuid is found
player_index = participants.index(puuid)
# Hopefully the name below is what you inputted into the first function!
print(match_data['info']['participants'][player_index]['summonerName'])
Once we’ve found where they are, we can print out some information specifically about them:
player_data = match_data['info']['participants'][player_index]champ = player_data['championName']
k = player_data['kills']
d = player_data['deaths']
a = player_data['assists']
win = player_data['win']print("Champ:", champ, "Kills:", k, "Deaths:", d, "Assists:", a, "Win:", win)
Gathering Data
For whatever we’re building, we may want to gather lots of data about a player. To make this easier, we’ll build some functions. Let’s start by converting the API calls we’ve already made:
# The first function simply gets the puuid, given a summoner name and region
# This is exactly the same as our first example, except we're building the API URL from scratch
def get_puuid(summoner_name, region, api_key):
api_url = (
"https://" +
region +
".api.riotgames.com/lol/summoner/v4/summoners/by-name/" +
summoner_name +
"?api_key=" +
api_key
)
print(api_url)
resp = requests.get(api_url)
player_info = resp.json()
puuid = player_info['puuid']
return puuid
# The function to get a list of all the match IDs (2nd example above) given a players puuid and mass region
def get_match_ids(puuid, mass_region, api_key):
api_url = (
"https://" +
mass_region +
".api.riotgames.com/lol/match/v5/matches/by-puuid/" +
puuid +
"/ids?start=0&count=20" +
"&api_key=" +
api_key
)
print(api_url)
resp = requests.get(api_url)
match_ids = resp.json()
return match_ids
# From a given match ID and mass region, get the data about the game
def get_match_data(match_id, mass_region, api_key):
api_url = (
"https://" +
mass_region +
".api.riotgames.com/lol/match/v5/matches/" +
match_id +
"?api_key=" +
api_key
)
resp = requests.get(api_url)
match_data = resp.json()
return match_data
Now, let’s say we want to look at our last 20 games and extract the data. To do this, we simply loop through the list of match IDs and perform the “get_match_data” function. We can then add information about these individual matches to a dataset, then convert that into a dataframe using the pandas package.
import pandas as pd # the "as" part just renames it to make it quicker to typedef gather_all_data(puuid, match_ids, mass_region, api_key):
# We initialise an empty dictionary to store data for each game
data = {
'champion': [],
'kills': [],
'deaths': [],
'assists': [],
'win': []
}
for match_id in match_ids:
print(match_id) # run the two functions to get the player data from the match ID
match_data = get_match_data(match_id, mass_region, api_key)
player_data = find_player_data(match_data, puuid) # assign the variables we're interested in
champion = player_data['championName']
k = player_data['kills']
d = player_data['deaths']
a = player_data['assists']
win = player_data['win'] # add them to our dataset
data['champion'].append(champion)
data['kills'].append(k)
data['deaths'].append(d)
data['assists'].append(a)
data['win'].append(win)
df = pd.DataFrame(data) df['win'] = df['win'].astype(int) # change this column from boolean (True/False) to be integers (1/0)
return df
Our dataframe should look something like this:
We can then find out some interesting information about the account, like the average statistics per Champion:
# Get the averages per champion
df.groupby('champion').mean()
Adding custom arguments to API calls
In the last section, we evaluated the account on 20 games. These also would have included any ARAM and Normal games played recently, which we may not want.
So, let’s go back to the original function and add some additional arguments to customise what the API will return.
# Updated function where you can set which queue to take data from
def get_match_ids(puuid, mass_region, no_games, queue_id, api_key):
api_url = (
"https://" +
mass_region +
".api.riotgames.com/lol/match/v5/matches/by-puuid/" +
puuid +
"/ids?start=0" +
"&count=" +
str(no_games) +
"&queue=" +
str(queue_id) +
"&api_key=" +
api_key
)
print(api_url)
resp = requests.get(api_url)
match_ids = resp.json()
return match_ids
We’ve added a new custom variable for the amount of games we want to see (no_games), as well as limited the data to only games from a specified queue ID (queue_id).
For Ranked Solo Queue, the Queue ID is 420.
You can go through the portal to find full information on the custom arguments available.
Rate Limits
If we went back to the developer portal’s homepage we can see under the API key that our “RATE LIMITS” are currently set to 20 per second & 100 per 2 minutes.
We can test this by doing this (completely pointless) loop:
# NOTE, we're sending off the same match ID 200 times, which isn't useful and only for the example
# yours may break before you reach 100, depending how fast you're going through the notebook
for i in range(200):
print("Attempt:", i)
match_data = get_match_data(match_id, mass_region, api_key)
print("Match Duration:", match_data['info']['gameDuration'])
The code will run UNTIL the 101st attempt, at which point it will fail. If we inspect the match_data at this point, we find a status_code of 429, meaning we’ve hit the rate limit.
So, let’s update our match data function to handle this type of error:
# Updated function to sleep until it's succesful
def get_match_data(match_id, mass_region, api_key):
api_url = (
"https://" +
mass_region +
".api.riotgames.com/lol/match/v5/matches/" +
match_id +
"?api_key=" +
api_key
)
# we need to add this "while" statement so that we continuously loop until it's successful
while True:
resp = requests.get(api_url)
# whenever we see a 429, we sleep for 10 seconds and then restart from the top of the "while" loop
if resp.status_code == 429:
print("Rate Limit hit, sleeping for 10 seconds")
time.sleep(10)
# continue means start the loop again
continue
# if resp.status_code isn't 429, then we carry on to the end of the function and return the data
match_data = resp.json()
return match_data
The code will now first check to see if the API is responding with a 429 error code. If it does, we use the time package to sleep the function for 10 seconds and then try again. The “while” loop ensures the code keeps repeating until we’ve successfully gone through all match IDs.
Now if we went back and repeated the totally pointless loop, once we hit the 101st attempt the code will begin printing “Rate Limit hit” until the doors are open again and we can get the data.
NOTE, if you’re going through this code quickly then you may hit these rate limits before the 101st attempt — since you’re still within the 2 minute window since you ran the previous requests before the loop.
Wrapping it all up
We can now pull our code together and get all the data together at once:
def master_function(summoner_name, region, mass_region, no_games, queue_id, api_key):
puuid = get_puuid(summoner_name, region, api_key)
match_ids = get_match_ids(puuid, mass_region, no_games, queue_id, api_key)
df = gather_all_data(puuid, match_ids, mass_region, api_key)
return dfsummoner_name = "Jack J"
region = "euw1"
mass_region = "EUROPE"
no_games = 100
queue_id = 420# Takes 2 minutes to run
df = master_function(summoner_name, region, mass_region, no_games, queue_id, api_key)# Let's do something interesting with the data# print some introductory stuff
print("Hello", summoner_name, "of", region.upper()) # upper simply capitalises the region
print("Here are some interesting statistics about your last 100 solo ranked games")# create a count column
df['count'] = 1# the "agg" allows us to get the average of every column but sum the count # see?
champ_df = df.groupby('champion').agg({'kills': 'mean', 'deaths': 'mean', 'assists': 'mean', 'win': 'mean', 'count': 'sum'})# we reset in the index so we can still use the "champion" column
champ_df.reset_index(inplace=True)# we limit it to only champions where you've played 2 or more games
champ_df = champ_df[champ_df['count'] >= 2]# create a kda column
champ_df['kda'] = (champ_df['kills'] + champ_df['assists']) / champ_df['deaths']# sort the table by KDA, starting from the highest
champ_df = champ_df.sort_values('kda', ascending=False) # ascending determines whether it's highest to lowest or vice-versa# assign the first row and last row to a variable so we can print information about it
best_row = champ_df.iloc[0] # .iloc[0] simply takes the first row in dataframe
worst_row = champ_df.iloc[-1] # .iloc[-1] takes the last row in a dataframeprint("Your best KDA is on", best_row['champion'], "with a KDA of", best_row['kda'], "over", best_row['count'], "game/s")
print("Your worst KDA is on", worst_row['champion'], "with a KDA of", worst_row['kda'], "over", worst_row['count'], "game/s")# sort by count instead
champ_df = champ_df.sort_values('count', ascending=False)# get your most played champ
row = champ_df.iloc[0]# assign and format the win rate
win_rate = row['win']
win_rate = str(round(win_rate * 100, 1)) + "%"print("Your highest played Champion is", row['champion'], "with", row['count'], 'game/s',
"and an average Win Rate of", win_rate)4# finally, sort by highest kills in a game (note, not using the champ_df groupby anymore but the raw data)
highest_kills = df.sort_values('kills', ascending=False)
row = highest_kills.iloc[0]
print("Your highest kill game was with", row['champion'], "where you had", row['kills'], "kills")
The output will look something like this:
What Now?
With that, we’ve concluded our introduction to the Riot API
To help you think of potential ideas, I’ll list a few of the popular APIs that you can use:
- How much Mastery a player has on each Champion
- In-depth game detail for every minute of the game (i.e. how much Gold/XP each player has at 12 minutes)
- In-depth objective and kill data, like who killed who, when and where.
- Ranked information, such as their current rank for each queue
- Who is currently in Challenger, Grand Master & Master (& every queue below that too!)
- And much more…!
There’s also further advanced topics that I haven’t covered in this introduction but you should be aware of:
- Advanced error handling, 429 Rate Limits is just one of many and each requires it’s own logic
- Speeding up your code! Eventually, you may decide to build something that requires a lot of data and async/multiprocessing/threading will help
- Building and hosting the frontend of the application
- Getting Riot to approve your application for public use
You got to the end of the article! My name is Jack J and I’m a professional Data Scientist applying AI to competitive gaming & esports. I’m the founder of AI in Esports start-up iTero.GG. You can follow me on Twitter, join the iTero Discord or drop me an e-mail at jack@itero.gg. See you at the next one.