RTRT.me - Real Time Race Tracking

Real-Time Race Tracking for World-Class Events

Web API Reference ( Updated 2023-01-05 ) CHANGELOG
  • 2023-01-05
    - New feature to retrieve splits and places for a single participant (or list) in the context of a category query.
  • 2019-02-03
    - Unicode chars in JSON are no longer escaped.
  • 2019-04-01
    - When specifying the querystring 'fields=' in Profiles or Splits call, every field requested will be returned even if there is no value for the field. Fields without values will be returned as empty strings.
  • 2019-02-03
    - Unicode chars in JSON are no longer escaped.
  • 2018-12-17
    - Access > HTTPS is now required for all requests.
  • 2018-02-14
    - Selection > Points > Get info about a specific point always returns 'list' object instead of single item.
  • 2017-02-03
    - Selection > Categories > mention of 'timesort' feature
  • 2016-09-11
    - Profiles - Estimated Locations > Updated feature to include GPS coords of estimated point.
  • 2015-01-02
    - Profiles - Estimated Locations > Added feature.
    - Authorization - Note about token expiration.
  • 2014-10-25
    - Polling > Eliminated the unused 'ugt' polling method.
  • 2014-09-14
    - Selection > Profiles - Noted that more than one in response is possible on numeric search.
    - Sorting & Other Considerations > Caching - Revised cache checking method--use pidversion.
  • 2013-09-06
    - All values returned in API are "strings". Cast values as needed in your application.
    - All queries now support 'fields' option in querystring to limit which fields are in response.
  • 2013-07-08
    - Turned off browser=1 mode by default. Use of JSON Formatter extension preferred.
  • 2012-12-20
    - Selection > Profiles > Add a split points query special keyword 'LATEST_POINT'.
  • 2012-09-05
    - Selection > Profiles > Add a parameter of 'namesort=1' This will order names in result by last, then first when doing /profiles?search queries.
    - Selection > Profiles > Add a parameter of 'failonmax=1' When results reach your max, instead of returning the restult, we will return the following error object.

The RTRT.me API allows developers to query event data via a public JSON+REST interface.

Access

When accessing the API, use HTTPS and provide your Application ID in the query-string of the URL.

https://api.rtrt.me/?appid=[YOUR APP ID]

Example: https://api.rtrt.me/?appid=4c5d9d5ef469f69057f7766a


Authorization

In addition to the Application ID, any client using the API must have for a unique client ID. This is called the token.

To acquire a token, the client must make a register call to the following URL:

https://api.rtrt.me/register?appid=[YOUR APP ID]

Example: https://api.rtrt.me/register?appid=4c5d9d5ef469f69057f7766a

The response will be a newly assigned token. For example:

{
	"token":"89ad3f6700e2e68e6431315bdab00f54"
}

Use the token and appid in all future requests.

Example: https://api.rtrt.me/?appid=4c5d9d5ef469f69057f7766a&token=89ad3f6700e2e68e6431315bdab00f54

Tokens may expire after 30 days. Please contact us if you require a longer-living token.

NOTE: Throughout this document, example hyperlink's do not have appid or token visible in the query-string for the sake of readability. However, they are included in the href.


Selection

You can select event data by requesting a URL and parsing the JSON response.

NOTE: When doing queries in a standard web browser, we recommend installing a JSON formatting extension to help make the response readable. Optionally, you can force the format to 'pretty' JSON by passing in the querystring "&browser=1".

Below is a list of available queries. Follow the links for example responses.

Events http://tile.rtrt.me/docs/api/rest#event-query

Get list of all events (that I have access to).

/events

Example: https://api.rtrt.me/events

Get details on a specific event.

/events/[EVENT NAME]

Example: https://api.rtrt.me/events/EVENTDEMO

You can further control which fields you want to return by using the fields querystring option and a comma separated list fields to return.

/events/[EVENT NAME]?fields=name,shortName,desc

Example: https://api.rtrt.me/events/EVENTDEMO?fields=name,shortName,desc

NOTE: When using the fields querystring parameter, every field requested will be returned even if there is no value for the field. Fields without values will be returned as empty strings.

Points

Get list of pre-defined locations (or "points") associated with a specific event.

/events/[EVENT NAME]/points

Example: https://api.rtrt.me/events/EVENTDEMO/points

Get info about a specific point.

/events/[EVENT NAME]/points/[POINT NAME]

Example: https://api.rtrt.me/events/EVENTDEMO/points/5K

Get all splits that have been reported for a specific point.

/events/[EVENT NAME]/points/[POINT NAME]/splits

Example: https://api.rtrt.me/events/EVENTDEMO/points/5K/splits

NOTE: For simplicity, all field values in the API are returned as strings. Type casting should be handled by your application when needed. For an explanation of fields, see field definitions.

You can specify more than one point name in a comma separated list to acquire a combined result.

/events/[EVENT NAME]/points/[POINT NAME,POINT NAME,POINT NAME]/splits

Example: https://api.rtrt.me/events/EVENTDEMO/points/5K,10K,13.1M/splits

You can use a special keyword of 'ALL_POINTS' to get splits for all points.

/events/[EVENT NAME]/points/ALL_POINTS/splits

Profiles http://tile.rtrt.me/docs/api/rest#profiles

Get list of profiles and search for profiles (participant names, bibs, and other profile information).

/events/[EVENT NAME]/profiles

Example: https://api.rtrt.me/events/EVENTDEMO/profiles

You can also search for participants by name. [QUERY] matches part of name based on a set of keywords. Search can be done on any combination and in any order such as 'first last' or 'last first', 'first middle', etc. Searching can also be enabled for 'city','charity' or 'team' if enabled for the event.

NOTE: Make sure to url encode spaces or other characters in your search request.

/events/[EVENT NAME]/profiles?search=[QUERY]

Example: https://api.rtrt.me/events/EVENTDEMO/profiles?search=jam

If [QUERY] is a number, a lookup on that bib number will be performed. Normally, only a single participant will be returned when a numeric bib or tag number matches. However, it is possible for more than one result on a number search if tag numbers are not the same as bib numbers, or if there are duplicate numbers in the event.

/events/[EVENT NAME]/profiles?search=103

Example: https://api.rtrt.me/events/EVENTDEMO/profiles?search=103

NOTE: After numbers are assigned, each profile should have both a 'tag' and a 'bib'. A searches will match 'bib' instead of 'tag'. Tags are internal IDs, often zero padded such as "0000212", and are not to be used for public display. See profile fields for more info.

Sometimes, when searching profiles, it makes sense to limit results instead of dealing with pagination of result sets. You can set a limit using the max option along with the special failonmax option.

/events/[EVENT NAME]/profiles?search=[QUERY]&max=50&failonmax=1

When failonmax is enabled, and the number of matched profiles is equal to or greater than the max, you will receive an error object instead of the result set.

"error":{
  "type":"too_many_results",
  "msg":"Max results reached and 'failonmax' is true. Not returning results"
}

This error should prompt the user to be more specific in the search. You might have a dialog which says something like

"Sorry, too many participants matched your search. Try entering more of the name, or try entering the bib number only."

While, generally, it is recommended that sorting of small result be performed client-side, as a convenience, we have added the option of namesort. Setting this parameter to 1 will order names in result by last name, then first name when doing profiles search queries only.

NOTE: You may not use 'namesort' when "polling", and you should only use 'namesort' in combination with the 'failonmax' parameter.

Example: https://api.rtrt.me/events/EVENTDEMO/profiles?search=j&max=50&failonmax=1&namesort=1

You can control which fields you want to return by using the fields querystring option and a comma separated list fields to return.

/events/[EVENT NAME]/profiles?fields=tag,name,age,sex

Example: https://api.rtrt.me/events/EVENTDEMO/profiles?fields=tag,name,age,sex

Get roster profile by 'pid' (participant identifier) or by 'tag' number.

/events/[EVENT NAME]/profiles/[PID or TAG #]

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/RBXW4TA5

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/103

NOTE: A 'pid' is an 8 character alpha-numeric unique identifier beginning with the letter 'R' that is assigned to a participant. Example "RBXW4TA5". This is designed not to change for this participant even when tag number assignments change. Therefore, it is usually best to query the API using 'pid' instead of tag #.

NOTE: Internally, a 'tag' will be zero padded such as "00103". However, when referencing a profile tag in the API here, you do not need the zeros, i.e. "/103" will work.

Get roster profiles for a list of 'pids' or 'tags'.

/events/[EVENT NAME]/profiles/[LIST of PIDs or TAG #'s]

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,RBXW4TA5

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/102,103

Get all splits (points) that have been reported for a specific profile or list of profiles.

/events/[EVENT NAME]/profiles/[LIST of PIDs or TAG #'s]/splits

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/103/splits

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,RBXW4TA5/splits

Get split info on a single specific point, list of points, or latest point for a profile.

/events/[EVENT NAME]/profiles/[LIST of PIDs or TAG #'s]/splits/[POINT NAME]
or
/events/[EVENT NAME]/profiles/[LIST of PIDs or TAG #'s]/splits/[LIST of POINT NAMES]

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/RBXW4TA5/splits/5K

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,RBXW4TA5/splits/5K,10K,13.1M

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,RBXW4TA5/splits/LATEST_POINT

Profiles - Estimated Locations http://tile.rtrt.me/docs/api/rest#profiles-est-locations

It is possible to retreive current estimated location information for participants by adding loc=1 to your querystring while requesting profiles/XXX or profiles/XXX/splits. This can be done with or without polling.

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,R6VF8KRP/splits/LATEST_POINT?loc=1&etimes=1&ecoords=1

During the race or during a simulation, the info section will return a 'loc' object for each participant queried if they have started the race.

{
   "list":[{ ... }],
   "info":{
      "first":"1",
      "last":"1",
      "loc":{
	"R2KRY3TV": {
		"multiplier": "1",
		"epc": "26.9",
		"emiles": "7.058",
		"mph": "6.52",
		"sslp": "466",
		"sss": "3918",
		"lpn": "10K",
		"npn": "10M",
		"etimes":{
			"10M": {
				"label": "10M",
				"time": "01:19:01",
				"timeOfDay": "8:21:19 am",
				"coords": {
					"lat": "40.620478",
					"lng": "-74.029275"
				}
			},
			"13.1M": {
				"label": "HALF",
				"time": "01:43:31",
				"timeOfDay": "8:45:49 am",
				"coords": {
					"lat": "40.624718",
					"lng": "-74.027538"
				}
			},
			"20M": {
				"label": "20M",
				"time": "02:38:02",
				"timeOfDay": "9:40:20 am",
				"coords": {
					"lat": "40.648120",
					"lng": "-74.010817"
				}				
			},
			"FINISH": {
				"label": "FINISH",
				"time": "03:27:10",
				"timeOfDay": "10:29:28 am",
				"coords": {
					"lat": "40.650961",
					"lng": "-74.007864"
				}				
			}
		}
		"ecoords": {
			"lat": "41.603477",
			"lng": "-87.930223"
		}
	}
   }
}

Meaning of 'loc' fields:

FieldDescription
multiplier Speed multiplier for simulation. Only changes when simulations are running. Normally, multipler is 1 (realtime).
epc Estimated Percentage of Completion. Gives a percentage of the participant's progress based on the course rounded to 1 decimal place. For example, a value of "50.1" would be slightly past the half-way point.
emiles Estimated Miles. Gives an estimated distance in miles traveled by the participant as measured from the start of the course. Multiply by 1.60934 for est. kilometers.
mph Miles Per Hour. Estimated current speed of participant. Multiply by 1.60934 for kilometers per hour.
sslp Seconds Since Last Point. Duration since seen at most recent point ('lpn').
sss Seconds Since Start. Duration since starting.
lpn Last Point Name. Name of most recent published timing point.
npn Next Point Name. Name of next upcoming published timing point.
overdue Overdue Seconds. Seconds since participant was expected to have arrived at the next point. Only available if participant is overdue.
ecoords Estimated Coordinates. The estimated latitude and longitude of the particpant based upon projected movement along course. Only returned if coordinates can be determined, otherwise, 'ecoords' field will not be included in the response. Must pass additional querystring parameter of ecoords=1 on each request where this feature is needed.
etimes Estimated Times. The estimates for each upcoming point. Etimes for each pid can only be returned if the current estimated location can be determined. If not, some pids may excluded or, of no pids have esimates, the 'etimes' field will not be included at all in the response even if requested.

The coords field is only availabe when map tracking is enabled and map is plotted in RTRT.me. Must pass additional querystring parameter of etimes=1 on each request where this feature is needed.

NOTE: Not all fields defined are always present. For example, 'npn' would not be available after finished.

ALWAYS CHECK FOR EXISTENCE OF FIELDS BEFORE USING.

Missing Participants: If a participant's data is not arriving as expected within a reasonable leeway, or if we can no longer estimate a location for any reason, 'emiles' and 'epc' will return with a value of "-1". Also, ecoords and etimes will not be available.

Categories http://tile.rtrt.me/docs/api/rest#categories

Categories are predefined as needed for a specific event.

Get a list of available categories.

/events/[EVENT NAME]/categories

Example: https://api.rtrt.me/events/EVENTDEMO/categories

Get list of participants in a category. The "fields=[field name, field name]" option is available as shown for the "/profiles" query above.

/events/[EVENT NAME]/categories/[CATEGORY NAME]

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women

Get splits for a category.

/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits

Example: https://api.rtrt.me/EVENTDEMO/categories/top-women/splits

Get splits for a single point, or list of points for a category.

/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits/[POINT NAME]
or
/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits/[LIST of POINT NAMES]
or
/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits/LATEST_POINT

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/5K

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/5K,10K,13.1M

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/LATEST_POINT

As a convenience, there is an option of timesort. Setting this parameter to 1 will ensure order of splits returned is by time. May only be used in 'categories' queries and not while 'Polling'.

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/5K?timesort=1

Get splits and places for a single participant (or list) in the context of the category.

/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits/[POINT NAME (OR CSV)]/[PID or TAG # (OR CSV)]
or
/events/[EVENT NAME]/categories/[CATEGORY NAME]/splits/ALL_POINTS/[PID or TAG # (OR CSV)]

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/5K/RLB6XW9J

Example: https://api.rtrt.me/events/EVENTDEMO/categories/top-women/splits/ALL_POINTS/RLB6XW9J?timesort=1

NOTE: The 'place' field is not availabe on start points and will not be returned. If you require empty key/value, use fields=[LIST OF FIELDS] to mandate items for response.


Pagination

Whenever you make a request that may have more than one result, the API will return an array called "list" that contains each of your results.

The default maximum number of results returned is 20. You may specify a different max in the query-string like so:

/events/[EVENT NAME]/points?max=5

NOTE: Highest max for anonymous requests is 1000. There is no limit when using authenticated tokens. Max set to 0 yields unlimited results for authenticated tokens. See Admin APIfor info about authenticated requests.

With any list, the API will also return an object called "info". Info will include stats on the query.

"info": {
	"first":1,
	"last":5,
}

With the exception queries on 'splits' and 'profiles', you can skip to a different set of results by adding start=### to your querystring.

/events/[EVENT NAME]/points?start=5&max=5

To skip through 'splits' or 'profiles', see 'Polling' below.

PLEASE NOTE: It is possible to get a count of the total objects in the list by adding 'total=1' to your querystring. However, it is best to develop without relying on 'total'. Totals are not guaranteed to be exactly precise at any given moment, and there is a slight performance cost from calculating them.


Polling http://tile.rtrt.me/docs/api/rest#polling

"Polling" can be done for splits or profiles. Polling in our case just means going back to see what is new since the last time you received any results. There are 2 polling methods:

agt - to fetch all newly inserted or updated records matching your query. (most common)
igt - to fetch only newly inserted records. (appropriate for live notification feeds)

Use one of these option in querystring to initiate polling. For example:

/events/[EVENT NAME]/categories/top-women/splits/5K?agt=0

Starting with agt=0 will bring in splits starting from the earliest insertion or update.

In the response, the "info" section will return the 'lasta', which is the insert or update stamp of the last record in the current set of results.

Now, to poll for the next set of results, you can use agt=[LASTA] in your querystring parameter. This will pull any results where insertion or update is greater than the value specified.

For example, let's say you have been polling, and in your last set of results, you had a 'lasta' of "1296493798_000281". Your next request should look like this:

/events/[EVENT NAME]/categories/top-women/splits/5K?agt=1296493798_000281

NOTE: If there are no results returned, you will receive an error object with a type of "no_results" on each request.

Depending on how much data you expect to receive, and how large of a response you want to be able to handle, you will likely want to increase the max parameter. NOTE: The default max is 20 records per request.

/events/[EVENT NAME]/categories/top-women/splits/5K?agt=1296493798_000281&max=100

If you are only interested in new inserts, and do not want any updated record info (appropriate for something like sending notifications), you would use the 'igt' polling method instead. For Example:

/events/[EVENT NAME]/categories/top-women/splits/5K?igt=1296493798_000281

In the response, the "info" section will return the 'lasti', which is the insert 'i' stamp of the last record in the current set of results.

NOTE: Split or profile records includes an 'i' or 'a' field. These fields are a stamp indictating the order of insertion or update, and consist of a combination of an insertion timestamp and a unique increment. For example:

"i":"1318783929_000007" - is the 7th insert which occurred within the unix timestamp 1318783929.

Reverse

On splits, you can also request the list to be in reverse order by adding reverse=1 to your querystring.

This would give the 3 most recent top women spits at the 5K.

/events/[EVENT NAME]/categories/top-women/splits/5K?max=3&reverse=1

Sorting & Other Considerations

Sorting

During an event, splits usually become available to the system in logical order. However, order is not always guaranteed due to a number of factors.

For example, it is possible that the 5K point becomes available AFTER the 10K if the device transmitting the data at the 5K was temporarily disconnected from the network.

Another example, if two participants cross the same point at very close to the same time, order of appearance in the API can sometimes be reversed. A participant with the better time by hundreds of a second may appear in the API after a participant with a slower time.

Since sorting splits by time in the API is not compatible with polling, sorting should be done by your application client-side. We recommend sort by the 'time' or 'timestamp' fields when displaying lists of splits for a participant or in a leaderboard. The 'time' field is independent of when the point was inserted or updated.

Caching

You may be considering caching the results locally on your client or in your system somewhere. Caching data locally adds complexity since changes can occur in the RTRT platform after it has been received by your application. For this reason, we usually recommend an on demand approach to retrieving data for most use cases.

However, some applications need to cache or store data. Polling for updates and inserts (the agt model) takes care of many caching concerns, but polling alone will not inform you of 'deleted' split or profiles.

To aid with caching, we provide the following:

Profile Versions

The version of a profile is stored as the '_ver' field. This field will advance whenever 1) profile information has changed such as name, bib, city, etc. or 2) the participants 'split' data has been modified or updated or after first insertion.

Passing querystring parameter of 'pidversion=1' to a query for profile splits will yield a breakdown of the requested profiles version info as demonstrated below.

Get all splits for specific profile or list of profiles along with profile version info.

/events/[EVENT NAME]/profiles/[PID or LIST of PIDs]/splits?pidversion=1

Example: https://api.rtrt.me/events/EVENTDEMO/profiles/R2KRY3TV,RBXW4TA5,RZ9ZWPFS/splits?pidversion=1

This yields a regular list or error object, but with an added section to 'info'. For example:

{
	"list":[{ ... }],
	"info":{
		"first":"1",
		"last":"2",
		"lasti":"1324666245_000008",
		"pidversion": {
			"R2KRY3TV": "11",
			"RBXW4TA5": "13",
			"RZ9ZWPFS": "-1"
		}
	}
}

The info section now provides a 'pidversion' which is an object with keys of pids requested and values of each profile's current version.

If no record is found for a requested pid, the pid version will be equal to -1, e.g. RZ9ZWPFS: "-1". In this case, the profile has been removed from the system and you will likely want to remove the profile and all split data from your system.

A good strategy is to keep track of the '_ver' as returned in 'profiles' queries, and also pass 'pidversion=1' on every request to get splits. Then, check the 'pidversions' on each response. If you find that they do not match (or are flagged for deletion), you should uncache & reload or delete all data for that particular participant.


Errors

If for some reason your request either returns no results, or is invalid for any reason, the API will respond with an "error" object.

{
	"error":{
		"type":"[ERROR_TYPE]",
		"msg":"[ERROR MESSAGE]"
	}
}

Example: https://api.rtrt.me/oops

Example: https://api.rtrt.me/events/NOTHINGHERE

NOTE: For a list of possible errors, see error types.