Skip to content

Old API overview

These examples show how to authenticate and give a few examples for working with our old APIs. This won't be supported forever, so please switch over to the new approach

Old APIs, case and style difference

Since summer 2020, OpenTrack is using a new API gateway at https://test-api.opentrack.run/ (remove the "test-" to work on live).

This uses the FastAPI web framework, connected to the main OpenTrack application database. This implements the OpenAPI specification, so there is a rich family of tools to help you use it.

We are also exporting static files nightly, initially competition calendars such as the file which powers the European Athletics web calendar; these are the same json as retrieved through the API

Prior even to that, we had an old API for downloading results, and one endpoint for uploading results; two federations have written code to export against this. We used 'camelCase' variables because we were advised this was more normal in the Javascript world, but this created headaches. However, internally our databasec and Django code uses the under_score_separated convention, and the API tools work with this automatically, so all APIs going forward will use the new style

Remember:

On data.opentrack.run  ->  'fullName', 'ageGroups' etc

On api.opentrack.run  ->  'full_name', 'age_groups' etc

On file.opentrack.run (static exports): ->  'full_name', 'age_groups' etc

Getting access

To access the API, you need an account on OpenTrack, so if you have not used it before, go to our site and sign up. The API generates a documentation page which you can click on and explore from a browser, to try the various queries. This is available in Swagger and ReDoc formats; we use the first one in screenshots below.

Connecting and making queries

We have illustrated our examples with the Unix CURL command, but you can also call APIs with wget or libraries in any language.

A very simple query you can do is to call our 'ping' API, to check it's alive; this requires no authentication:

$ curl -X GET "https://test-api.opentrack.run/api/v1/ping/" -H  "accept: application/json"

{
  "reply": "pong",
  "enviroment": "otr_0"
}

Or, with Python's requests library,

Authorizing and getting a token

Almost all API calls require a token. You make an initial request for a token, sending your username and password; and then use this token as a request header on subsequent requests. Tokens are valid for 24 hours.

Here is a complete runnable script to query competitions, with some filter parameters, using a token, and Python's requests library.

import requests

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

r = requests.get(
    "https://test-api.opentrack.run/api/v1/competitions/", 
    params=dict(country="MLT", from_date="2020-09-01"),
    headers=headers
    )
competitions = r.json()
print("Retrieved %d competitions in Malta" % len(competitions))

API Examples

If you visit opentrack-docs/api_examples/ you will see several files, the code in the above example can be run by calling

python 
import authorise_api
authorise_api.example_test()

Alternatively there are several other tests that can be run detailed below, however before running them there is something important to do and to know.

1. For testing purposes make sure to use test-api.opentrack.run instead of api.opentrack.run, this will make sure you don't accidently delete anything important.

2. Create a file in the /api_examples/ folder called secrets.py containing the following 2 lines, this will be needed for testing, just fill in your email and password.

EMAIL = '<your email>'
PASSWORD = '<your password>'

Querying competition calendar entries

Our competition calendar has light, simple records for each competition. You can query these easily with request parameters. For example, this returns all competitions in Norway since 1st August 2020:

GET https://test-api.opentrack.run/api/v1/competitions?country=MLT&from_date=2020-08-01&to_date=2020-08-10

[
    {
        'full_name': '2020 MAAA Championships Young Athletes / Challenge 2 Senior', 
        'short_name': 'MAAA Champ Young - Chall  2 Sen', 
        'slug': 'championships-young-20-chal-2', 
        'name_local': '', 
        'country_id': 'MLT', 
        'address': '', 
        'city': '', 
        'venue_id': 'ebd3ec4b-4782-41ca-9895-3c298971c711', 
        'latitude': None, 
        'longitude': None, 
        'altitude': None, 
        'date': '2020-08-01', 
        'finish_date': '2020-08-02', 
        'wa_rankings_category_id': 15, 
        'age_groups': ['U12,U14,U16,U18,U20,U23,SEN'], 
        'national_id': '', 
        'basic_description': '', 
        'contact_details': '', 
        'organiser_id': '19c65443-9b0f-444d-b874-1e74d97310fb', 
        'website': None, 
        'entry_link': None, 
        'results_link': None, 
        'override_token': None, 
        'id': '0279cc68-ccff-4ca3-bd1d-0da86fa05cc0', 
        'year': 2020, 
        'events': None
    },
  .... # competitions omitted
 ]

Alternatively run python get_competitions.py whilst in the /api_examples/ directory after having set up the secrets.py file described previously to run a get request for some Malta competitions.

If you wish to power a calendar and load data without authenticating, we also offer files built every night and stored in Amazon in exactly the same format. We publish these for the CURRENT and FOLLOWING year.

https://file.opentrack.run/live/euroath/european_calendar_2020.json
https://file.opentrack.run/live/euroath/european_calendar_2021.json
https://file.opentrack.run/live/euroath/domestic_calendar_GBR_2020.json
https://file.opentrack.run/live/euroath/domestic_calendar_GBR_2021.json

These are suitable for embedding or calling from other off-site pages (i.e. the storage bucket has no CORS headers), and will be available even when we are doing server maintenance

Creating new competitions and calendar entries

We can expose the API to national-level systems providers. In many countries (e.g. Germany, Spain, Italy, France) there is a "national system" which contains most or all competitions; we aim to make it easy for these people to register a competition in Opentrack, tell us their ID, and get our ID for the competition.

Your user account needs to be given admin privileges for the country in question. We do ths by assigning your user to a group called ADMIN_XXX on the server, where XXX is the ISO country code. Thus, if you want rights for Estonia, your user must be in the ADMIN_EST group.

Conceptually there MIGHT be a two-step process, although it is usually one, as we need to check very carefully that we are not creating duplicates.

The following example can be called simply from the opentrack-docs/api_examples directory by calling python create_competition_and_calendar_entries.py after having activated your virtual environment and creating the secrets.py file described in step 2 of API Examples

Example

import requests
import json

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

comp = {
    "full_name": "EAP - Malta International Meet",
    "short_name": "",
    "slug": "malta-inter-meet",
    "name_local": "",
    "country_id": "MLT",
    "address": "Marsa Sports Complex MRS 9065 Aldo Moro Road, Marsa, Malta 12. Matthew Micallef St John Stadium Mars",
    "city": "",
    "venue_id": None,
    "latitude": None,
    "longitude": None,
    "altitude": None,
    "date": "2021-05-08",
    "finish_date": "2021-05-08",
    "wa_rankings_category_id": 10,
    "age_groups": None,
    "national_id": "",
    "basic_description": "",
    "contact_details": "malta@eap-circuit.org",
    "organiser_id": "19c65443-9b0f-444d-b874-1e74d97310fb",
    "website": None,
    "entry_link": None,
    "results_link": None,
    "override_token": None,
    "id": "ef59d4b4-4d16-49ae-b824-7a23e8a0027d",
    "year": 2021,
    "events": []
}

r = requests.post(
    "https://test-api.opentrack.run/api/v1/competitions/", 
    data=json.dumps(comp),
    headers=headers
    )

print("Created competiton with status_code %d" %  r.status_code)
parsed = json.loads(r.text)
print(json.dumps(parsed, indent=4, sort_keys=False))

Duplicates

As mentioned before, the API carefully checks there are not similar competitions already in our database. If it is the case, a special error message will be returned.

{
    "detail": {
        "error": "A competition with the same name and date exists",
        "override_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgzNDc1NjkuOTgxODY3LCJuYmYiOjE2MTI3OTkxNjl9.6c0UtY6TM4-3aZf1Zo51M05IFr5HDVEaLdXSIiJH7LQ",
        "mathing_competitions": [
            {
                "full_name": "EAP - Malta International Meet",
                "short_name": "",
                "slug": "malta-inter-meet",
                "name_local": "",
                "country_id": "MLT",
                "address": "Marsa Sports Complex MRS 9065 Aldo Moro Road, Marsa, Malta 12. Matthew Micallef St John Stadium Mars",
                "city": "",
                "venue_id": null,
                "latitude": null,
                "longitude": null,
                "altitude": null,
                "date": "2021-05-08",
                "finish_date": "2021-05-08",
                "wa_rankings_category_id": 10,
                "age_groups": null,
                "national_id": "",
                "basic_description": "",
                "contact_details": "malta@eap-circuit.org",
                "organiser_id": "19c65443-9b0f-444d-b874-1e74d97310fb",
                "website": null,
                "entry_link": null,
                "results_link": null,
                "override_token": null,
                "id": "ef59d4b4-4d16-49ae-b824-7a23e8a0027d",
                "year": 2021,
                "events": null
            }
        ]
    }
}

If this happens you have two options.

  • Option 1: the competition you wanted to create is a duplicate. Use the provided id (ef59d4b4-4d16-49ae-b824-7a23e8a0027d in the example) and use one of the other API endpoints to update the competition
  • Option 2: the competition you wanted to create is new. Use the provided override_token (eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MjgzMzY1MjAuMDUyOTQxLCJuYmYiOjE2MTI3ODgxMjB9.qmDtJvolitfcQ8YDyst2f3b1yAUqvGF5mhcjGbA_SBg in the example) and add it as a new field in the comp dictionary above. NOTE: the token has a 3 days validity.

Query, Update, Delete a known competition

If you know the id of a competition, you can interact directly with the competition.

Query a competition

import requests

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

comp_id = "868aadc8-f9bb-49bc-952f-25c16eadb69e"
r = requests.get(
    f"https://test-api.opentrack.run/api/v1/competitions/{comp_id}", 
    headers=headers
    )

Update a competition

An important part is to remember to get the id of the country based on how it is on the competition url. So if your competition has /year/country_id/slug/ in the url on our website, you need to use the 3 letters in that url where country_id is within update_comp below, replacing id_of_country.

import requests
import json

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

update_comp = {
    'full_name': 'new_name',
    'country_id': 'id_of_country'
}

comp_id = "868aadc8-f9bb-49bc-952f-25c16eadb69e"
r = requests.post(
    f"https://test-api.opentrack.run/api/v1/competitions/{comp_id}",
    data=json.dumps(update_comp),
    headers=headers
    )

Delete a competition

import requests

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

comp_id = "868aadc8-f9bb-49bc-952f-25c16eadb69e"
r = requests.delete(
    f"https://test-api.opentrack.run/api/v1/competitions/{comp_id}", 
    headers=headers
    )

Test hardness endpoint

We provide a test endpoint to check whether your input respects our competition schema. The API call does not create a database entry, so feel free to use it intensively.

import requests
import json

EMAIL = '<your email>'
PASSWORD = '<your password>'

r = requests.post(
    "https://test-api.opentrack.run/api/v1/auth/token", 
    data={"username": EMAIL, "password": PASSWORD})

resp = r.json()

token = resp["access_token"]

headers = {'Authorization': 'Bearer %s' % token}

comp = {
    'full_name': 'Trond Mohn Games',
    'short_name': 'TMG',
    'slug': 'trond-mohn-games',
    'name_local': 'Trond Mohn Games',
    'country_id': 'NOR',
    'address': 'Grimseidvegen 60, 5239 RĂ¥dal, Norge',
    'city': 'Bergen',
    'venue_id': 'c3c87364-0ab4-44b5-a518-b143f574c086',
    'latitude': 60.2905,
    'longitude': 5.3178,
    'altitude': 60,
    'date': '2021-05-29',
    'finish_date': '2021-05-29',
    'wa_rankings_category_id': None,
    'age_groups': ['Men, women'],
    'national_id': 'NOR',
    'basic_description': '',
    'contact_details': 'Dag Rydland drydland@broadpark.no',
    'organiser_id': 'e893fa86-8c2e-4f99-a9f1-b6fcfb0a787c',
    'website': 'https://www.trondmohngames.no',
    'entry_link': 'http://www.trondmohngames.no',
    'results_link': 'http://en.trondmohngames.no/live-results/'
}

r = requests.post(
    "https://test-api.opentrack.run/api/v1/competitions/test", 
    data=json.dumps(comp),
    headers=headers
    )