RTRT.me - Real Time Race Tracking

Real-Time Race Tracking for World-Class Events

Web API Reference ( Updated 2014-09-14 ) CHANGELOG
  • 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.
  • 2012-05-19
    - Added 'fields' option to event and point queries.
    - Polling > Better Explanation.
  • 2011-12-27
    - Added comma list support for profile queries. This allowing for combined queries of multiple participants without using 'groups' feature.
    - Single objects from specific point or profile queries now yield 'list' object even in only single item is returned. This makes it easier to program when using the comma list queries by providing a consistant response structure.

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

Access

When accessing the API, you must provide your Application ID in the query-string of the URL.

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

Example: http://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:

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

Example: http://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: http://api.rtrt.me/?appid=4c5d9d5ef469f69057f7766a&token=89ad3f6700e2e68e6431315bdab00f54

Tokens remain available and do not expire throughout the lifecycle of an event.

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://rtrt.me/docs/api/rest#event-query

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

/events

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

Get details on a specific event.

/events/[EVENT NAME]

Example: http://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: http://api.rtrt.me/events/EVENTDEMO?fields=name,shortName,desc

Points

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

/events/[EVENT NAME]/points

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

Get info about a specific point.

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

Example: http://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: http://api.rtrt.me/events/EVENTDEMO/points/5K/splits

NOTE: All split fields in the API are returned as strings. 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: http://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://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: http://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: http://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: http://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: http://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: http://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: http://api.rtrt.me/events/EVENTDEMO/profiles/RZ9ZWPFR

Example: http://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 "RZ9ZWPFR". 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: http://api.rtrt.me/events/EVENTDEMO/profiles/RZAW7TRZ,RZ9ZWPFR

Example: http://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: http://api.rtrt.me/events/EVENTDEMO/profiles/103/splits

Example: http://api.rtrt.me/events/EVENTDEMO/profiles/RZAW7TRZ,RZ9ZWPFR/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]
or
/events/[EVENT NAME]/profiles/[LIST of PIDs or TAG #'s]/splits/LATEST_POINT

Example: http://api.rtrt.me/events/EVENTDEMO/profiles/RZ9ZWPFR/splits/5K

Example: http://api.rtrt.me/events/EVENTDEMO/profiles/RZAW7TRZ,RZ9ZWPFR/splits/5K,10K,13.1M

Example: http://api.rtrt.me/events/EVENTDEMO/profiles/RZAW7TRZ,RZ9ZWPFR/splits/LATEST_POINT

Categories

Categories are predefined as needed for a specific event.

Get a list of available categories.

/events/[EVENT NAME]/categories

Example: http://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: http://api.rtrt.me/events/EVENTDEMO/categories/top-women

Get splits for a category.

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

Example: http://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]

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

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


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: A max set to 0 will return unlimited results. Use with caution!

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

"info": {
	"first":1,
	"last":5,
	"cacheVer":"0~0"
}

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://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 3 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)
ugt - to fetch only existing records that have been updated.

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 'i' or 'u' 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 (ideal for something like SMS 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.

For updates only, you can use ugt=[LASTU] in your querystring parameter to pull any results where u is greater than the value specified.

NOTE: Use 'timestamp' for scoring and not 'i' or 'u' fields. The 'timestamp' field is the actual time reported by timing device. Split or profile record includes an 'i' or 'u' 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 ('i' or 'u').

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: http://api.rtrt.me/events/EVENTDEMO/profiles/RZAW7TRZ,RZ9ZWPFR/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",
		"cacheVer":"0~2",
		"pidversion": {
			"RZAW7TRZ": "11",
			"RZ9ZWPFR": "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: http://api.rtrt.me/oops

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

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