Using The Strava API With Python — Beginner Tutorial
It’s time to do something with all that data you have on Strava
Strava offers a powerful Web API that allows users to access and analyse data from their activities. Whether you’re a fitness enthusiast looking to track your progress or a developer interested in building a simple app that leverages activity data, the Strava API provides a wealth of possibilities.
In this tutorial, we’ll walk through how to use Python to interact with the Strava Web API. By the end of this guide, you’ll learn how to authenticate your app, gain authorised access to a user’s account, and retrieve activity data.
Before we get started, you will need a Strava account, and a Python environment set up on your machine. Any libraries used in this project that you don’t already have installed can be downloaded with pip.
Getting Started
The first step is to register an API application with Strava. You can find this option in the Strava settings in your browser, look for settings → api, or try the link below:
You can find detailed instructions from Strava for getting started with registering your application here.
When filling out the application form, the website you provide does not matter, and the call-back domain should be set to “localhost”.
Once the setup is complete, we are provided with a client id
and a client secret
. These are like the username and password for the application, and should both be kept secret.
You can copy these into your code as string literals, but instead, it is recommended that you set them as system environment variables. This way, you don’t run the risk of accidentally publishing them on GitHub for all the world to see. However, if your client secret is compromised, you can request a new one from Strava from the settings → api menu.
Setting Environment Variables
To add an environment variable in Windows 10/11, go to the start menu, search for “Edit the system environment variables” and hit enter. Once the window has opened, click on “environment variables”, then, under “user variables”, click on “new”. You can now add your client id in the pop-up window, as shown below. Do the same with the client secret. You may need to restart your system for these changes to take effect.
User Authentication Procedure
With the API application set up, we can now start the authentication procedure. Strava uses a protocol called OAuth2 for authentication. This allows users to grant our application access to their data without giving away their login credentials. Strava outline the procedure here, but the steps are as follows:
- The application sends a request to the server. This request contains the client id (application username) and a list of scopes (which are the permissions that we are requesting).
- The user is directed to Strava’s OAuth page prompting them to sign in and authorise the application with the permissions requested.
- The user is then directed to the given
redirect_uri
(see code below) where they are given an authorisation code (embedded in the URL). - This code should be entered into our application. With it, we send another server request to retrieve an access token.
- This token works as a temporary password, granting permission to access the user’s account. Whenever we want to fetch some data from a Strava profile, we will send this token, along with the request, in order to verify that we have the user’s permission.
If you just want a simple, one-time-use application, you can stop here. However, every time the application is used, the user will have to sign in and grant access to their account again. To make this more user-friendly, we need to make use of the refresh token
.
When we requested the token, we were sent an access token and a refresh token. An access token is used to authorise a data retrieval request, and will expire after a set time period, usually 6 hours. A refresh token, as the name suggests, is used to refresh our access token without needing the user to sign in again and reauthorise the app.
Implementation
Let’s now take a look at the authentication procedure in Python. We will use the requests
library throughout this project to send requests to the server. This library is not specific to the Strava API, it can be used with any valid web API.
import os
import requests
import webbrowser
import json
def initial_authorisation_token():
# client id and client secret can be given as string literals, but it
# is recommended to set them as environment variables instead, we can
# then access them as shown here
client_id = os.environ['STRAVA_CLIENT_ID']
client_secret = os.environ['STRAVA_CLIENT_SECRET']
# url the user is directed to once they have logged in
redirect_uri = 'http://localhost:8000'
# send request to server to authorize a user
# this will prompt the user to sign into strava and grant our application permissions
request_url = f'http://www.strava.com/oauth/authorize?client_id={client_id}' \
f'&response_type=code&redirect_uri={redirect_uri}' \
f'&approval_prompt=force' \
f'&scope=profile:read_all,activity:read_all'
# open url in browser
webbrowser.open(request_url)
# recieve code once user has logged in
code = input('Insert the code from the url: ')
# Get the access token
token = requests.post(url='https://www.strava.com/api/v3/oauth/token',
data={'client_id': client_id,
'client_secret': client_secret,
'code': code,
'grant_type': 'authorization_code'})
token = token.json()
# save token for later (function shown below)
write_token(token)
return token
After logging in, the user is prompted to authorise the application, granting the permission we requested. These permissions, “profile:read_all”, and “activity:read_all”, are the scopes we sent in the request (see code above).
The exact scopes needed for a project will depend on the data we want to access from the user’s account. You can find a full list of scopes here, under “Details About Requesting Access”.
After inputting the code into our application, it will send a request for the token. When we request some data from the server, we pass the access_token
with it in order to authenticate the request.
This is what the token looks like (in JSON format). It contains an expiration time, access token, refresh token, and various other fields.
# token format
{'token_type': 'Bearer',
'expires_at': 1725636635,
'expires_in': 21556,
'refresh_token': '***************************************',
'access_token': '***************************************',
...
}
To save this token for future use, we can write it to a file:
# save the token in a file
def write_token(token):
with open('strava_token.json', 'w') as file:
json.dump(token, file)
… and read it back later:
# read token from file
def read_token():
try:
with open('strava_token.json', 'r') as f:
token = json.load(f)
except FileNotFoundError:
# token cannot be found, so cannot be refreshed
# instead, follow the original authorisation procedure again
token = initial_authorisation_token()
return token
As mentioned before, this is all we need for a simple one-time-use application. However, to make this reusable, we need to utilise the refresh token to ensure that the access token is always up to date. This should still work days or even weeks after receiving the original token.
The access token should be refreshed before it is used to request data, otherwise, if it has expired, the server will return an error message. We can check if the token has expired by comparing the current time to the expiration time given in the token.
import time
# refresh the authorisation token
def refresh_token(token):
# check if the token has expired
if token['expires_at'] < time.time():
# request a new access token using the refresh token
token = requests.post(url='https://www.strava.com/api/v3/oauth/token',
data={'client_id': client_id,
'client_secret': client_secret,
'grant_type': 'refresh_token',
'refresh_token': token['refresh_token']})
token = token.json()
write_token(token)
return token
Using the Access Token
Now we have the tools to be able to access a user’s data, let’s try importing a list of all of their activities.
We send a request to https://www.strava.com/api/v3/athlete/activities
accompanied with the access token, and some other parameters. If everything is correct, it will return a list of activities.
However, much like on a website where not all results are shown in one go, this request will return only the first page of activities. We can then request page 2, then page 3, and so on, until we are returned a blank page. At this point, we will know that we have gathered the entire list.
import pandas as pd
# get & update token first, uses tools described above
token = read_token()
token = refresh_token(token)
# import all user activities
activities = []
page = 1
response = []
while True:
# request new page of activities
endpoint = f"https://www.strava.com/api/v3/athlete/activities?" \
f"access_token={token['access_token']}&" \
f"page={page}&" \
f"per_page=50"
response = requests.get(endpoint).json()
# check if page contains activities
if len(response):
# retrieve some fields for each activity
# you can see the full list of fields by looking at the response json
activities += [{"name": i["name"],
"distance": i["distance"],
"type": i["type"],
"sport_type": i["sport_type"],
"moving_time": i["moving_time"],
"elapsed_time": i["elapsed_time"],
"date": i["start_date"],
"polyline": i["map"]["summary_polyline"],
"map_id": i["map"]["id"],
"start_latlng": i["start_latlng"]} for i in response]
page += 1
else:
break
# convert our activities to a DataFrame
df = pd.DataFrame(activities)
We now have a database of activity data that we can use in whichever way we want.
Usage Limits
Looking back at the Strava API settings page in the browser, we see that there is a limit to the number of requests that we can send to a server. We are restricted to only 100 read requests per 15 minutes, and 1000 per day.
Therefore, it can be worth saving your activities to a file so that you don’t need to retrieve all of them again every time you use the application. Instead, since they are listed with the most recent ones first, you can request new ones until you find a duplicate that you already have saved.
It’s also worth noting that you can only have 1 user set up on your application. Trying to get a second user to sign in will result in an error message from the server. The only way for someone else to use this code would be to have them set up their own application, generate a client id and client secret, and use those values in your code instead. These measures are taken to prevent people making commercial applications using the Strava API.
We now have the ability to unlock insights about activities, training and performance. We can use this to create custom visualisations, analyse progress, build leader boards, and create progress trackers. The possibilities are endless.
Happy coding!