Strava: update gear using strava api

Introduction

I started to use Pro bike garage to track the parts of my bike. But the app can only match activites for which the gear is set. I started to using the gear feature in Strava when using a second bike for activities. So before that (December 2018 in my case) all activities have no gear set.

This blog post describes how I updated all past activites and set the gear_id for them.

The Python code here is using Python 3.6+ and requests.

You need your own Strava api application. For more information on your own app look at http://developers.strava.com/ and for your CLIENT_ID and CLIENT_SECRET go to your api page at https://www.strava.com/settings/api.

Get an access_token

The Getting started from Strava is pretty helpful here: http://developers.strava.com/docs/getting-started/.

The scope is set to read and write activites. Private activites are not included (this would need activity:read_all).

The same steps as in the guide but using Python:

import requests

params = {
    "client_id": <CLIENT_ID>,
    "response_type": "code",
    "redirect_uri": "http://localhost",
    "approval_prompt": "force",
    "scope": 'read,activity:read,activity:write',
}

response = requests.get("http://www.strava.com/oauth/authorize", params=params, allow_redirects=False)
print(response.text)

Now copy the url into your browser and authorize. The redirect goes to a localhost server that doesn't work but the code in the url is all we need.

The next step is to get the access_token by using the code given by the url:

data = {
   "client_id": <CLIENT_ID>,
   "client_secret": "<CLIENT_SECRET>",
   "code": "<CODE>",
   "grant_type": "authorization_code",
}

response = requests.post(url="https://www.strava.com/oauth/token",
                         data=data)
access_token = response.json()["access_token"]

All following api calls will use this access_token. The token has an expiration date which seems to be currently a day. Enough to update all activites we want to update.

Update the gear of selected activites

All api calls withh use the access_token in their header, so set a header:

headers = {
   "Authorization": f"Bearer {access_token}"
}

To get an ACTIVITY_ID browse your strava activities. First we need one with the gear already set and then one without a gear set.

Now get one activity with the gear already set to get the gear_id.

activity = requests.get(url="https://www.strava.com/api/v3/activities/<ACTIVITY_ID>",
                        headers=headers)

gear_id = activity.json().get("gear_id")

To test that everything works with one activity let us update the gear for another activity that has no gear set.

activity = requests.get(url="https://www.strava.com/api/v3/activities/<ACTIVITY_ID>",
                        headers=headers)

# should return None
print(activity.json().get("gear_id"))

# update only the gear of that activity
response = requests.put(url=f"https://www.strava.com/api/v3/activities/{activity.json()['id']}",
                        data={"gear_id": gear_id},
                        headers=headers)

# should return the expected gear_id
print(response.json().get("gear_id"))

Update the gear of all old activites without gear set

We loop over all activities of the user and update the bicyle rides that have no gear. I have about 2200 rides and some other activities. So I set the loop range to 24 pages of data.

for page in range(1, 25):
    print(f"-- page: {page}")
    params = {"per_page": 100, "page": page}
    result = requests.get(url="https://www.strava.com/api/v3/athlete/activities",
                          params=params, headers=headers)
    for item in result.json():
        if item.get("type") == "Ride" and item.get("gear_id") is None:
            print(f"update {item.get('id')}")
            activity = requests.put(url=f"https://www.strava.com/api/v3/activities/{item.get('id')}",
                                    data={"gear_id": gear_id},
                                    headers=headers)
            print(activity.json().get("gear_id"))
The outer loop iterates over the pages of activities we want to update (100 per page).
The inner loop iterates over the activities of the athlete on that page.
Each activity is checked if no gear is set and the activity is a Ride.
If that is the case the gear_id is set for that activity.

The script ran quite some time but was still to fast for the rate limit (which is 600 requests per 15 minutes). When the rate limit was hit I waited for 15 minutes and started the for loop again.

The waiting time for the updates to finish was long enough to write this post.