Welcome to the Triathlon API
Developer Hub

Whether you are a researcher, media, or a fan, you'll find comprehensive guides and documentation to help you get up and running with the Triathlon API.

Read the docs Register & manage your app

Live Timing Standard v1.1

Version 2 of Live Timing now available for 2019!

Version 2 of the Live Timing API is now available. We highly recommend using this to benefit from better validation and error responses. Click here for more details.

The previous version will remain available and you may use this as a fallback in case of any issues with v2.

Version 1.1 of the Live Timing Standard will be mandatory for all World Triathlon Series events

For World Cup and other events the implementation of the standard will be optional but implementing will enable live timing to be shown on Triathlon.org as well as other custom-built applications.

Version 1.1 of the live timing standard builds upon the original version 1 of the live timing standard with some notable changes in order to enhance the timing delivery. We have attempted to maintain as much consistency as possible with the prior version to ease the upgrade process. The most notable changes are:

  • All results are now provided in a single JSON output rather than the separate leaderboard and splits files that were previously required.
  • All athletes (and corresponding split data) are now included in the timing output regardless of the position of the athlete. This differs in that, in the prior version, only athletes that had crossed the latest timing point were included in the output.
  • Timers now have the option of either hosting the JSON file or making a POST request to ITU servers with the timing data if it is preferred to eliminate the requirement to host any files (see delivery options for an enhanced explanation).
  • Race incidents such as DNFs are now able to be included which may be publicly viewable on a race incidents page.
  • This timing standard removes the requirement for timers to upload intermediate results to Triathlon.org for ranking generation purposes.
  • Live timing is now available to all ITU events that support the live timing standard.

Overview

The aim of Version 1.1 of the Triathlon.org Live Timing Standard is to produce a higher quality of real-time timing data directly from races which may be delivered via the Triathlon.org API platform to subscribers.

The improvements in the standard greatly increase the potential for displaying timing data and developing feature-rich timing applications when used in conjunction with other available APIs. The standard also removes the requirement for timers to host any web-facing files as timing may be pushed to the Triathlon.org API platform which will handle the delivery of timing messages to all connected clients. This push method of delivery is entirely optional as the standard also allows for a single timing file hosted by the timers to be accessed solely by ITU's servers and is not public facing (see delivery options).

Timing Message Format

Each timing message should contain complete timing information such that a user may be able to retrieve all information by receiving the latest timing message. All message characters should be UTF-8 formatted. All times and dates should be formatted according to the ISO 8601 standard and millisecond data should be included if available. Data should only be included if available e.g. segment values should only be included once an athlete has completed the segment. Data types are not strictly enforced to ensure maximum compatibility and it is recommended to transmit all data as strings.

Example Messages

The following messages only contain 2 athletes for brevity and you should refer to the extended examples below for full timing messages.

{
   "date":"2017-03-04T18:25:11+04:00",
   "start_time":"2017-03-04T15:55:00+04:00",
   "event_id":"107158",
   "event_name":"2017 ITU World Triathlon Abu Dhabi",
   "prog_id":"307158",
   "prog_name":"Elite Men",
   "wetsuit":"false",
   "sandbox":"false",
   "num_athletes":"2",
   "status":"live",
   "latest":{
      "segment_id":"2",
      "segment_name":"Swim Exit",
      "num_athletes":"1"
   },
   "conditions":{
      "air_temp":"28.8",
      "water_temp":"23.2"
   },
   "athletes":[
      {
         "id":"5695",
         "start_num":"26",
         "name":"Javier Gomez Noya",
         "country":"ESP",
         "segment_id":"18",
         "time":"00:18:37.911",
         "difference":"00:00:00.000",
         "position":"1",
         "status":null,
         "incidents":null,
         "splits":[
            {
               "id":"1",
               "name":"Swim Lap 1",
               "total_time":"00:10:54.799",
               "time":"00:10:54.799",
               "difference":"00:00:11.001",
               "position":"10"
            },
            {
               "id":"2",
               "name":"Swim Exit",
               "total_time":"00:18:37.911",
               "time":"00:07:43.112",
               "difference":"00:00:11.320",
               "position":"14"
            }
         ]
      },
      {
         "id":"18719",
         "start_num":"22",
         "name":"Thomas Bishop",
         "country":"GBR",
         "segment_id":"1",
         "time":"00:10:53.996",
         "difference":"00:00:10.198",
         "position":"2",
         "status":null,
         "incidents":null,
         "splits":[
            {
               "id":"1",
               "name":"Swim Lap 1",
               "total_time":"00:10:53.996",
               "time":"00:10:53.996",
               "difference":"00:00:10.198",
               "position":"9"
            }
         ]
      }
   ]
}

Complete Timing Examples

A full timing example in JSON is available here.

Demonstration Links

A very basic front-end implementation of an XML sample file is available at the following links:

Timing Parameters

A breakdown of each parameter is listed below.

Parameter
Example
Description
Format

date

2017-01-01T13:34:59.123Z

An ISO 8601 formatted date of the time the message was published

string

start_time

2017-01-01T13:34:59.123Z

An ISO 8601 formatted date corresponding to the start time of the race

string

event_id

123456

The unique event_id corresponding to the event_id in the Events API

integer

event_name

2016 ITU World Triathlon Abu Dhabi

A descriptive name for the event corresponding to the Events API

string

prog_id

123456

The unique prog_id corresponding to the prog_id in the Events API

integer

prog_name

Elite Men

A descriptive name for the race corresponding to the Events API

string

wetsuit

true

Boolean to indicate whether it is a wetsuit swim

boolean

sandbox

true

Boolean to indicate whether to use the sandbox environment for development (defaults to true)

boolean

num_athletes

12

The number of athletes in the race (which must correspond to the number of items in the athletes array)

integer

status

live

The status of the results (one of prerace, live, official). Once confirmed by race referee the status should be updated to official. Prior to the race a status of prerace should be used and once timing has started changed to live.

string

latest

The latest object contains information regarding the latest timing point data (may be null if no timing data)

object

latest.segment_id

1

The segment_id containing the latest result

integer

latest.segment_name

Swim Exit

The segment name containing the latest results

string

latest.num_athletes

12

The number of athletes that have passed through the timing point

number

conditions

An object containing the latest course conditions

object

air_temp

12.5

The official air temperature in degrees Celsius

float

water_temp

18.4

The official water temperature in degrees Celsius

float

athletes

An object containing an array of all athlete objects in the race

object

athletes.athlete

An object containing the timing data for an individual athlete

object

athlete.id

123456

The unique athlete_id corresponding to the athlete_id in the Athletes API

integer

athlete.start_num

12

The start number of the athlete

integer

athlete.name

Alistair Brownlee

The name of the athlete

string

athlete.country

GBR

The three letter IOC country code

string

athlete.segment_id

1

The latest segment_id of the athlete

integer

athlete.position

12

The position of the athlete (may be null if no data) at the latest timing point

number

athlete.time

00:17:16.145

An ISO 8601 formatted time of the athlete's latest timing point

string

athlete.difference

00:01:13.134

An ISO 8601 formatted time corresponding to the difference at the latest timing point

string

athlete.status

DNF

An optional field to include additional information regarding the race status of the athlete which should be one of DNS, DNF, DSQ, LAP or NC

string

athlete.incidents

10 second penalty for bike infringement

An optional field to include additional information regarding any incidents such as penalties that an athlete has incurred formatted according to the standard incident guidelines.

string

athletes.splits

An array of split objects containing data for each segment completed by the athlete (may be null if no data)

object

athletes.splits.segment

A segment object containing athlete timing data for each individual segment.

object

segment.id

3

The segment_id of the split

integer

segment.name

Swim Exit

The name of the timing segment

string

segment.time

00:08:16.345

The time to complete this segment

string

segment.total_time

00:14:12.345

The cumulative time of all segments to this point

string

segment.position

12

The position of the athlete at this timing point

integer

segment.difference

00:01:07.123

An ISO 8601 formatted time of difference of the athlete to the leader at this timing point

string

Sending JSON data

From 2018 onwards you may only send JSON formatted data and you must include a header of Content-Type set to application/json.

Athlete Ordering

Athletes should be ordered in the athletes array according to the position at the respective timing points. Naturally, athletes who have passed the latest timing point will be ordered first followed by previous timing points. At the start of the race or where ordering is not possible due to non-completion of segments athletes should by default be ordered by start number.

Default to ordering by start_num where no further ordering is possible

Timing Points

Segment IDs should be named according to the convention below and increase sequentially for each segment. The first timing segment will always be 1.

Example Standard Distance Race

Segment ID
Segment Name

0

Start

1

Swim Lap 1

2

Swim Exit

3

Transition 1

4

Bike Lap 1

5

Bike Lap 2

6

Bike Lap 3

7

Bike Lap 4

8

Bike Lap 5

9

Bike Lap 6

10

Bike Lap 7

11

Bike End

12

Transition 2

13

Run Lap 1

14

Run Lap 2

15

Run Lap 3

16

Finish

Example Sprint Distance Race

Segment ID
Segment Name

0

Start

1

Swim Exit

2

Transition 1

3

Bike Lap 1

4

Bike Lap 2

5

Bike Lap 3

6

Bike End

7

Transition 2

8

Run Lap 1

9

Finish

Timing Delivery

There are two options for timing delivery namely a hosting of a static file and a POST method. Choice of timing delivery is up to the individual timer.

Option #2 is preferred

Whilst we offer static file hosting to support existing timers the POST method of delivery is much preferred and should be chosen where it is an option.

1 - Static File Hosting

This method replicates the prior version of the timing standard where a timer hosts a single JSON file and this is polled by the Triathlon.org servers (note this is not a public facing application and only accessed by Triathlon.org which acts as a proxy). The URL format is completely discretionary as long as it is accessible on the internet (you may protect the file if required such as using basic authentication or a token).

Timing updates should be provided as quickly as possible whilst maintaining consistency. Updates to the file should preferably only be made when new timing information is available. Whilst the lower latency the better any update frequency under 10,000ms (10 seconds) will be deemed acceptable.

2 - POST JSON Data

This method removes the requirement for a timer to host any files as the JSON is simply POSTed to the Triathlon API (https://api.triathlon.org/v1/timing) on any change of data i.e. an athlete crossing a timing mat.

Timing messages should be delivered as soon as feasible. Data messages should be sent as soon as an athlete crosses a timing point or when an external event occurs (such as the race start or a penalty is awarded). For occasions when there would be an overwhelming number of updates e.g. a large bike pack crossing a timing point, messages should be combined into a single message such that push frequency does not exceed one push every 1000ms. If POSTing data, messages should only be pushed when there is a change of information in the message and messages should not be sent according to a fixed time schedule.

Messages should be sent with a Content-Type of application/json.

Authentication

Timers will use the standard API authentication method adding authentication parameters to the header of all requests sent. Each timer will be provided a unique API key to use specific to their timing application. This API key may alsop be used to access all other public endpoints of the API such as start lists.

Response Codes

The Triathlon.org API will respond with a 2xx response on successful receipt of the message. All other response codes should be considered a failure. Badly formatted or unauthenticated messages will result in a 4xx code. For reference, our general response codes are available here.

Retry Policy

Timers should implement a retry policy to primarily cover the case of a loss of internet connectivity at the venue. As each message contains entire timing information if a later message is available there is no need to retry an earlier message. For reference you may also consult the retries policy of the Subscription API.

Message Persistence

Timers do not need to persist timing messages as this will be handled via the Triathlon API platform with specific messages available for historical analysis / replay via the Live API.

Concurrent Races

Often there will be more than one race occurring at the same time (defined by prog_id). Each race should be considered a single entity and formatted according to the specification. Logic should be present to handle instances when multiple athletes from different races cross a timing point at the same time.

Timing Implementation

  • An initial start list message will be published once the final start lists have been confirmed (which occurs at the race briefing). This will detail the start_time, the event_id and prog_id as well as containing the data for each athlete on the start list (you may use the athlete_id to get further information for each athlete from the Athletes API). Any athletes that withdraw between the briefing and the start of the race are considered DNS athletes.
  • Any changes to the published start times, condition information or any DNS (indicated in the `athlete.status' field) will be indicated by a new timing message.
  • Any messages sent prior to the start of the race will be indicated by a status of prerace.
  • As soon as the race begins the start_time containing the accurate start time will be published and the status will be updated to live indicating that the race is underway.
  • As soon as the first athlete crosses the first timing point the latest block will be updated to include the details of the segment (i.e. segment_id and segment_name) as well as how many athletes have crossed the timing point (which may be used to create leaderboards). Whilst timing messages are pushed whenever there is new data, on occasions timing messages will be combined into a single message e.g. a bike pack crossing a timing point will not have a message for each athlete. As each timing message always contains full timing information you should simply act on the latest message received.
  • This process continues through each segment of the race with the latest block always indicating the latest timing point. Any athlete that withdraws from the race, is disqualified or incurs a penalty should have their status updated accordingly.
  • Once the race has finished and the results are confirmed a final message with a status of official will be published.

Other Timing Notes

  • If an athlete is disqualified during the race, the status of the athlete should be updated to DSQ and the athlete should be ordered at the bottom of the athlete array. The reason for disqualification should be added to the incidents key using the standard incident guidelines. Athletes should only be set as DSQ on confirmation from the Chief Technology Officer. As disqualified athletes may continue on the course (in lieu of appealing the decision) all disqualified athlete times should continue to be delivered in the timing messages. If an athlete successfully protests the DSQ the athlete should be reinstated in the corresponding position in the athlete array once confirmed by the Chief Technology Officer.
  • If an athlete withdraws from the race, once confirmed by the Chief Technology Officer, the status of the athlete should be updated to DNF. If a DNF reason is available it should be added to the incidents key according to the format specified by the standard incident guidelines.
  • If an athlete has a penalty confirmed by the Chief Technology Officer the incidents key of the athlete object should be updated to indicate the penalty to be served in accordance with the standard incident guidelines. Once an athlete has served the penalty, and has been confirmed by the Chief Technology Officer, the incidents key should be updated by prepending the word Served e.g. Served x second penalty for infringement name.
  • If the timer does not send millisecond data but instead only delivers times as full seconds, the raw millisecond data should be rounded up to the next full second.

For example:
00:09:14.000 -> 00:09:14
00:09:14.001 -> 00:09:15
00:09:14.500 -> 00:09:15
00:09:14.999 -> 00:09:15
00:09:15.000 -> 00:09:15

Standard Incident Guidelines

When adding penalty or disqualification information the following guidelines should be followed for reporting as notified by the Chief Technology Officer. The message should be updated to include when the penalty has been served.

  • x second penalty for infringement name
  • Served x second penalty for infringement name
  • DSQ for infringement name

Examples:

  • 15 second penalty for cutting the swim course
  • Served 15 second penalty for cutting the swim course
  • DSQ for cutting the swim course
  • Withdrew from the race due to bike crash.

In the event where there are multiple incidents for an athlete in an individual program, multiple incidents should be included and delimited by a comma.

Mixed Relay Timing

Mixed Relay timing follows a very similar syntax as individual timing with an additional layer of information which comprises team member data. As with the individual data, all athletes and teams should be included in all timing messages.

The following example shows a curtailed response of a single team with, in this example, the second athlete on the team to have exited the swim portion of the race.

{
   "date":"2017-06-11T17:03:12+0000",
   "start_time":"2017-06-11T14:39:00+0000",
   "event_id":"107161",
   "event_name":"2017 ITU World Triathlon Hamburg",
   "prog_id":"307248",
   "prog_name":"Elite Mixed Relay",
   "wetsuit":"true",
   "sandbox":false,
   "num_teams":1,
   "status":"live",
   "latest":{
      "segment_id":"8",
      "segment_name":"Leg 2 Swim Finish",
      "num_teams":1
   },
   "conditions":{
      "air_temp":"17.1",
      "water_temp":"16.6"
   },
   "teams":[
      {
         "id":"1234",
         "start_num":"1",
         "name":"Team GBR I",
         "country":"GBR",
         "segment_id":"8",
         "time":"00:49:51.000",
         "difference":"00:00:00.000",
         "position":1,
         "status":null,
         "incidents":null,
         "athletes":[
            {
               "id":"9876",
               "team_order":"1",
               "name":"Non Stanford",
               "country":"GBR",
               "splits":[
                  {
                     "id":1,
                     "name":"Leg 1 Swim Exit",
                     "total_time":"00:16:38.000",
                     "time":"00:08:20.000",
                     "difference":"00:00:05.000",
                     "position":7
                  },
                  {
                     "id":2,
                     "name":"Leg 1 Bike Start",
                     "total_time":"00:17:41.000",
                     "time":"00:01:04.000",
                     "difference":"00:00:02.000",
                     "position":4
                  },
                  {
                     "id":3,
                     "name":"Leg 1 Bike Lap 1 Start",
                     "total_time":"00:35:05.000",
                     "time":"00:17:24.000",
                     "difference":"00:00:00.000",
                     "position":1
                  },
                  {
                     "id":4,
                     "name":"Leg 1 Bike Finish",
                     "total_time":"01:15:04.000",
                     "time":"00:05:55.000",
                     "difference":"00:00:00.000",
                     "position":2
                  },
                  {
                     "id":5,
                     "name":"Leg 1 Run Lap 1 Start",
                     "total_time":"01:15:28.000",
                     "time":"00:25.000",
                     "difference":"00:00:00.000",
                     "position":2
                  },
                  {
                     "id":6,
                     "name":"Leg 1 Run Lap 1 Finish",
                     "total_time":"01:22:58.000",
                     "time":"00:07:31.000",
                     "difference":"00:00:00.000",
                     "position":1
                  },
                  {
                     "id":7,
                     "name":"Leg 1 Finish",
                     "total_time":"01:46:51.000",
                     "time":"00:08:10.000",
                     "difference":"00:00:00.000",
                     "position":1
                  }
               ]
            },
            {
               "id":"7788",
               "team_order":"2",
               "name":"Alistair Brownlee",
               "country":"GBR",
               "splits":[
                  {
                     "id":8,
                     "name":"Leg 2 Swim Exit",
                     "total_time":"00:16:38.000",
                     "time":"00:08:20.000",
                     "difference":"00:00:00.000",
                     "position":1
                  }
               ]
            },
            {
               "id":"5586",
               "team_order":"3",
               "name":"Vicky Holland",
               "country":"GBR",
               "splits": null
            },
            {
               "id":"11378",
               "team_order":"4",
               "name":"Jonathan Brownlee",
               "country":"GBR",
               "splits": null
            }
         ]
      }
   ]
}

Mixed Relay Notes

  • segment_ids should be consecutive and unique being indexed from 1 i.e. Leg 1 Swim Finish and Leg 2 Swim Finish are unique segment_ids.
  • Timing point names should follow the suggested format as indicated here but prefixed with the corresponding leg number i.e. Leg 3 Swim Exit
  • In the latest block num_teams is used as opposed to num_athletes
  • As with individual races messages, all teams and all athletes should be included in every message. If the athlete has not yet competed their splits value will be null

Paratriathlon

Where applicable a Paratriathlon object should be included within each athlete object to specify additional Paratriathlon-specific information such as guide names and additional meta information.

{  
    "athlete_id": "123456",
    .....
  	"paratriathlon":{  
      "class":"PTWC1",
      "guide_name":"Guide Name",
      "guide_id":"654321",
      "compensation_time":"00:03:46"
   }
}

A breakdown of each parameter is listed below:

Parameter
Example
Description
Format

paratriathlon

The paratriathlon object contains specific information regarding the paratriathlete and is only present for Paratriathlon events

object

paratriathlon.class

PTWC1

Indicates the Paratriathlon class as multiple classes may compete in a single program

string

paratriathlon.guide_name

Athlete Name

The name of the paratriathlon guide for visual impairment categories

string

paratriathlon.guide_id

123456

The unique athlete_id corresponding to the athlete_id in the Athletes API

integer

compensation_time

00:00:30

An ISO 8601 formatted time of the start time offset. May be set to null if not applicable.

string

Distribution

Timing will be distributed by the Triathlon API platform to applications through the Live REST API, Subscriptions API and Streaming APIs to all connected applications.