RTRT.me - Real Time Race Tracking

Real-Time Race Tracking for World-Class Events

Web API Reference ( Updated 2013-09-06 ) CHANGELOG
  • 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

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

Get list of profiles and search for profiles (participant names, tags 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. Search can also be done on the 'city' field in roster 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 tag number will be performed. Only a single participant will be returned when a tag number matches.

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

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

NOTE: It is also possible to have a different 'bib' than the RFID 'tag'. If event is configured as such, a numeric search will match 'bib' instead of 'tag'.

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 #.

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

"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, tags 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').

Polling & Caching

If you are Polling for splits or profiles, you are probably caching the results locally on your client or in your system somewhere. This is ideal, as caching results locally will improve performance of the system at all levels. Polling for updates and inserts (the agt model) takes care of many caching concerns, since any changes will received as they happen. However, there are special considerations when using caching. Mainly, if you are caching event configurations such as lists of points or categories, or for deleted records on polling requests, we provide a way to check for such changes.

There are 2 main cache checking methods.

First, there is a 'cacheVer' property which is returned in the info section of every query. The cacheVer consists of 2 integers separated by a tilde i.e. "2~252". The first integer represents a global cache key and advances only when there is a major change which could affect the overall system, such as new categories added, points changing, etc. If this global integer advances, you may want to reset your application, clearing cache and rebuilding any views such as category displays, etc. The second integer (after the tilde) is an indicator for the 'splits' or 'profile' data cache. This advances when there is a change in previously inserted splits or in a participant's roster info.

Secondly, in addition to cacheVer, which only tells you if something has changed, there is also a way to find out which tags were affected.

Get a complete list of all pids that have had changes since initial insertion for this event.

/events/[EVENT NAME]/check

This yields a regular list object of tags that have changed, and the 'i' (timestamp + incremental) of when the change occurred. For example:

{
   "list":[
      {
         "tag":"103",
         "pid":"RZ9ZWPFR",
         "i":"1324666202_000004"
      },
      {
         "tag":"712",
         "pid":"R6R6V3PZ",
         "i":"1324666245_000008"
      }
   ],
   "info":{
      "first":"1",
      "last":"2",
      "lasti":"1324666245_000008",
      "cacheVer":"0~2",
      "lastCheck":"1324666248"
   }
}

The info section provides a 'lastCheck' timestamp which is generated by the server when you make the request.

You may also use the igt option to perform polling on cache checks.

/events/[EVENT NAME]/check?igt=[LASTI or LASTCHECK]

Example: http://api.rtrt.me/events/EVENTDEMO/check?igt=1324666248

You can filter the results of check by providing specific tags or pid you are interested in with the tags or pids querystring.

/events/[EVENT NAME]/check?igt=[LASTI or LASTCHECK]&tags=[LIST OR RANGE OF TAGS]
or
/events/[EVENT NAME]/check?igt=[LASTI or LASTCHECK]&pids=[LIST OF PIDS]

Example: http://api.rtrt.me/events/EVENTDEMO/check?igt=1324666202&tags=2,5,100-800

Example: http://api.rtrt.me/events/EVENTDEMO/check?igt=1324666202&pids=R6R6V3PZ

NOTE: When using tags, you can specify a range such as '100-800'.

A good strategy is to monitor the data cache version, and upon advancing, check to see if any of your currently cached tags are affected since the last time you checked them by using the check command with a list of pids and the igt option.


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.