Skip to content

Real-Time Race Tracking® Statistics

Real-Time Race Tracking provides technology to accurately time races. They provide a website to live track athletes or view their result after the race.

Unfortunately they don't provide statistics about the whole race. This collection of scripts calculates the global statistics. Most of the api is well documented

import requests
import random
import json
from datetime import timedelta
from matplotlib import pyplot as plt

Every client needs a randomly generated uuid to interact with the api and an anonymous client is allowed to query 2000 records at once.

uuid = random.randbytes(10).hex().upper()
uuid
'57055175B94A616DB85C'

The event id uniquely defines the race, it is usually present in any URL to the event.

event_id = "RP-OSU-4MILER-25"
params = {"event":event_id,
          "sess": 0,
          "appid": "52139b797871851e0800638e",
          "token": uuid,
          "max": 2000,
          "start": 1,
          "source": "webtracker"
         }

We query 2000 runners until we reach the end and the last position is not the maxium one queried

data = []
res = requests.post(f"https://api.rtrt.me/events/{event_id}/places/overall/FINISH", data=params)
dat = res.json()
data += dat["list"]
while params["start"] + params["max"] -1 == int(dat["info"]["last"]):
    params["start"] += 2000
    print("check if there are more, starting at", params["start"])
    res = requests.post(f"https://api.rtrt.me/events/{event_id}/places/overall/FINISH", data=params)
    dat = res.json()
    data += dat["list"]
check if there are more, starting at 2001
check if there are more, starting at 4001
check if there are more, starting at 6001
check if there are more, starting at 8001

We save the data to easily reload it and don't need to scrape again.

with open(f"final-list-{event_id}.json", "w") as f:
    json.dump(data, f)

From the string data, we extract the time in seconds and the category they race in

times = []
for runner in data:
    t = runner["time"]
    secs = timedelta(hours=int(t[0:2]), minutes=int(t[3:5]), seconds=float(t[6:])).total_seconds()
    times.append((secs, runner["sex"]))

Optionally we search for a specific runner to show their position in the total statistics or show the fastest Karen

for i,p in enumerate(data):
    if "Karen" in p["name"]:
        break

t = data[i]["time"]
you_secs = timedelta(hours=int(t[0:2]), minutes=int(t[3:5]), seconds=float(t[6:])).total_seconds()
fig, ax = plt.subplots()

ax.hist([[t[0] for t in times if t[1]=="F"],[t[0] for t in times if t[1]=="M"]], bins=50, stacked=True, label=["F","M"])
ax.axvline(x=you_secs, color="r")
ax.set_xlabel("minutes")
ax.xaxis.set_major_formatter(lambda x, pos: f"{int(x//60)}:{round(x%60):02d}")
ax.set_title(event_id)
ax.legend(reverse=True)
ax.set_ylabel("number of runners finished")
plt.show()

png