Reverse Engineering Crossfit Games API

I am "competing" in the CrossFit Open this year. I have only been doing CrossFit for about 5 months and signed up for the open as a personal challenge and to see where I stack up against others in my age group.

It is 0500 in the morning and I want to see where I am at on the leaderboard. I log in to the CrossFit Games website to check. I can search for my name and see what place I am in, but the experience is odd and some information is missing.

As I navigate the leaderboard I notice the website is not reloading, it must be pulling this data from an API. Let's reverse engineer the API and get the data we want.

Finding the API

I start by launching the Developer Tools built into the Chrome Browser and going to the Network tab. If you are following along this is the leaderboard URL: https://games.crossfit.com/leaderboard/open/2022?view=0&division=2&region=0&scaled=0&sort=0

Just looking at the URL we can see some parameters we might be interested in: division, region, and scaled.

Loading the Leaderboard sends a lot of requests, to find the request to the API lets Filter and show Fetch/XHR requests.

The page is making 9 requests, one of them to the following URL: https://c3po.crossfit.com/api/competitions/v2/competitions/open/2022/leaderboards?view=0&division=2&region=0&scaled=0&sort=0

We found the API.

The Basics

In the CrossFit Open, there are three main categories you can compete in.

  • RX: You do the hardest version of the workout
  • Scaled: You do a slightly easier version of the workout
  • Foundations: You do the easiest version of the workout

Going back to Chrome I watch for outgoing requests to the API while changing the Workout Type Filter on the website. From that I determine how to use the scaled parameter on the API.

  • RX: scaled=0
  • Scaled: scaled=1
  • Foundations: scaled=2

In a similar way I map out some of the division parameter options:

  • Men: division=1
  • Women division=2
  • Men (35-39) division=18

The website allows me to search by Athlete Name, let's see how that works. As I type my name in the search box it begins autocompleting with the closest matching athlete name. I find the following request is sent as I am typing.

https://games.crossfit.com/competitions/api/v1/competitions/open/2022/athletes?term=Josh%20Gra&division=18

When I click on my autocompleted name the page makes the following request:

https://c3po.crossfit.com/api/competitions/v2/competitions/open/2022/leaderboards?view=0&division=18&region=0&scaled=1&sort=0&athlete=2166965&athlete_display=Joshua+Grant

The results of that request contains all the information I was looking for. I can see the total number of competitors based on the parameters of my request.

{
  "version": 3,
  "dataType": "LEADERBOARD",
  "query": "query-string",
  "sort": 0,
  "pagination": {
  "totalPages": 43,
  "totalCompetitors": 2140,
  "currentPage": 15
}

I can see that I am currently 729 out of 2140 for Males 35-40 that completed the scaled workout.

{
  "overallRank": "729",
  "overallScore": "12732",
  "nextStage": "",
  "ui": {
  "highlight": true,
  "countryChampion": false
},
"entrant": {
  "competitorId": "2166965",
  "competitorName": "Joshua Grant",
  "firstName": "Joshua",
  "lastName": "Grant",
  "status": "ACT",
  "postCompStatus": "",
  "gender": "M",
  "profilePicS3key": "20a29-P2166965_1-184.jpg",
  "countryOfOriginCode": "US",
  "countryOfOriginName": "United States",
  "countryShortCode": "",
  "regionId": "31",
  "regionName": "North America",
  "divisionId": "18",
  "affiliateId": "27760",
  "affiliateName": "CrossFit CG2RK",
  "age": "36",
  "height": "",
  "weight": "",
  "teamCaptain": "0"
}