REST and GraphQL APIs for Racing, Driving and Social event registration

Support

We offer a mailing list for Developer support and API announcements. Please subscribe to motorsportreg-api-developers at Google Groups.

Wordpress/Joomla Event Calendar Plugin

We have released free Wordpress & Joomla plugins for displaying an event calendar on your website. Learn more.

GraphQL API and Calendar Widget

See our GraphQL API and Calendar Widget announcement. Access the documentation here.

REST API

MotorsportReg.com provides a RESTful API. This is intended for programmers, developers and hackers with at least some technical skills who wish to access our system programmatically for integration with their own systems. This page outlines the resources are available to you, how to access them and some example code.

Authentication

Most requests require valid credentials. There are three ways to authenticate:

  1. OAuth 1.0 [Preferred]
    • We support three-legged OAuth 1.0a
    • Register your application by contacting us
    • Obtain a Request Token at /rest/tokens/request
    • Direct users to our authorization URL: https://www.motorsportreg.com/index.cfm/event/oauth
    • Exchange Request Token for Access Token at /rest/tokens/access
    • Make requests on behalf of users with their Access Token and an X-Organization-Id (for resources that require one)
  2. Use an existing administrative username and password
    • Good for one-off tasks or in-house scripts, not for sharing with 3rd party apps or developers
    • Each request must pass an X-Organization-Id header which contains the 35-character unique organization ID to access
  3. Use custom API credentials provided by MotorsportReg.com [Deprecated]

To request credentials for authentication, tell us what you're building.

For user agents that support it, we have a crossdomain.xml file located at https://api.motorsportreg.com/crossdomain.xml. We also respond to every request with the HTTP Header 'Access-Control-Allow-Origin' set to '*'. This permits building mashups with our data from any domain.

Licensing & Attribution

Use of the API is subject to prior approval and may be revoked at any time without warning. Content that is displayed must include one of the following snippets of code exactly as it appears in the html below. We may provide other sizes upon request.

MotorsportReg.com logos
Powered by MotorsportReg.com logos

Resources and Locations

A REST interface presents resources at published locations. Access them securely over SSL. Here are the locations of the MotorsportReg.com REST API resources.

Unauthenticated
GET /rest/calendars/organization/{organization_id}
Return a calendar of events for a single organization/club
Supports geospatial filtering by passing URL parameter "postalcode" with a five-digit American Zip Code or six-character Canadian Postal Code. Optionally specify "radius" which defaults to 300 miles. Optionally specify "country" using an ISO 3166-1 ALPHA-2 code (e.g. US, CA, AU) to specify the country.
Optionally pass URL parameter archive=true to include events in the past
Optionally pass URL parameters "start" and "end" dates to include overlapping events, e.g. ?start=2017-01-01&end=2018-01-01 to get events which begin or end between those dates
Optionally pass URL parameter exclude_cancelled=true to remove cancelled events from the results
Optionally pass URL parameter types with one or more comma-delimited type_ids to filter the results, e.g. ?types=x,y,z
GET /rest/calendars/organization/{organization_id}/type/{type_id}
Return a calendar for a single organization/club for a single event type
GET /rest/events/{event_id}/entrylist
Unauthenticated request returns the same data as the public entry list. The contents of the list are controlled by the organizer and may return a limited or empty result set. For the full entry list, authenticate first and use the assignments resource.
Requires OAuth Authentication

For an example of OAuth integration in Node.js, see this post.

POST /rest/tokens/request
Obtain a request token to be used in directing users to our authorization URL at https://www.motorsportreg.com/index.cfm/event/oauth
Requires signing only, no authentication
Per RFC 5849, response is returned in body as application/x-www-form-urlencoded
POST /rest/tokens/access
Exchange a request token for a permanent access token by passing the token and verifier code
Requires signing only, no authentication
Per RFC 5849, response is returned in body as application/x-www-form-urlencoded
GET /rest/me
Return the profile and all organization memberships of the user whose OAuth token the request is made of
Helps identify organizations to which the profile belongs and their IDs for use in X-Organization-Id headers
GET /rest/me/vehicles
Return a list of vehicles for the user whose OAuth token the request is made of
GET /rest/me/vehicles/{vehicle_id}
Return a single vehicle for the user whose OAuth token the request is made of
GET /rest/me/events
Return a list of event registrations for the user whose OAuth token the request is made of
Requires Authentication
GET /rest/calendars
All events from all calendars starting today or later (by default).
Supports geospatial filtering by passing URL parameter "postalcode" with a five-digit American Zip Code or six-character Canadian Postal Code. Optionally specify "radius" which defaults to 300 miles. Optionally specify "country" using an ISO 3166-1 ALPHA-2 code (e.g. US, CA, AU) to specify the country.
Optionally pass URL parameter archive=true to include events in the past
Optionally pass URL parameters "start" and "end" dates to include overlapping events, e.g. ?start=2017-01-01&end=2018-01-01 to get events which begin or end between those dates
Optionally pass URL parameter exclude_cancelled=true to remove cancelled events from the results
Optionally pass URL parameter types with one or more comma-delimited type_ids to filter the results, e.g. ?types=x,y,z
GET /rest/calendars/{event_id}
Get details about a single event
GET /rest/calendars/venue/{venue_id}
Return a calendar of events for a single venue (Laguna Seca, Road Atlanta, ...)
GET /rest/calendars/type/{type_id}
Return a calendar of events for a single type of event (HPDE, Autocross, Rally, ...)
GET /rest/postalcodes/{postal_code}
Return postal codes within 300 miles of the passed value. Optionally filter with URL parameter "radius" to another value.
GET /rest/events/{event_id}/attendees
Authenticated request returns a complete list of attendees including all statuses, order and payment totals.
Questions may be included in partial response by passing ?fields=questions in URL
Limit results with optional UTC-based timestamp ?registered_since=yyyy-mm-dd+10:00:00.000 or ?lastupdate_since=yyyy-mm-dd+00:00:00.000 in URL
Enable millisecond-level timestamp precision (recommended with since filters for cursor-like behavior) by passing ?precise_timestamps=true in URL
GET/PUT/DELETE /rest/events/{event_id}/attendees/{attendee_id}
Read, delete, or update status, notes and metadata for a single attendee
Questions may be included in partial response by passing ?fields=questions in URL
GET/PUT/POST/DELETE /rest/events/{event_id}/attendees/{attendee_id}/checkin
Read, create, update or remove the check in history and notes associated with an attendee
GET /rest/events/{event_id}/assignments
GET /rest/events/{event_id}/assignments/{assignment_id}
GET /rest/events/{event_id}/attendees/{attendee_id}/assignments
GET /rest/events/{event_id}/segments/{segment_id}/assignments
Authenticated request returns assignments (aka entries) including all statuses. Provides internal links to profiles and vehicles to obtain more details about an assignment.
Co-drivers/Teams may be included in partial response by passing ?fields=team in URL
Instructors may be included by passing ?fields=instructors in URL
Vehicle questions may be included by passing ?fields=vehicle_questions in URL
Vehicle modifications may be included by passing ?fields=modifications in URL
Flagtronics ID may be included by passing ?fields=flagtronics in URL
Contact information may be included in partial response by passing ?fields=profile in URL
Contact information and profile question answers may be included in partial response by passing ?fields=profile_questions in URL
GET /rest/events/{event_id}/feeds/timing [JSON only]
Return an array of changes to timing and scoring data such as names, vehicles, transponders, classes or numbers. Use this to relay changes from Registration to T&S operators for updates.
Filter data with optional parameters for ?segments={segment_id,segment_id,segment_id} in URL
Limit results with optional UTC-based timestamp ?since=yyyy-mm-dd+00:00:00.000 in URL
GET /rest/events/{event_id}/segments/{segment_id}/feeds/timing [JSON only]
Same as above but pre-filtered to a single segment of your event. Use the since parameter to limit results.
GET /rest/events/{event_id}/segments
Return a list of segments with number, class, modifier and group options
GET /rest/members
Return a list of all members
Member questions may be included in partial response by passing ?fields=questions in URL
Default avatar image is 80 pixels square, request different sizes by passing ?imagesize=xx in URL
Search for a particular profile by email by passing ?email=you@domain.com in URL
Search for a particular profile by member number by passing ?memberId=12345 in URL
Search for a particular profile by unique ID by passing ?uniqueId=100000 in URL
Filter by member type by passing ?types=a,b,c in URL. Get available types via /rest/members/types.
GET /rest/members/{member_id}
Return a single member
Registration history may be included in partial response by passing ?fields=history in URL
Member questions may be included in partial response by passing ?fields=questions in URL
Default avatar image is 80 pixels square, request different sizes by passing ?imagesize=xx in URL
PUT/DELETE /rest/members/{member_id}
Update or delete a member - allowed fields are memberId, memberEnd and status
Pass in an array of types to modify the member types
Pass in an array of questions to update the club/profile question answers (file/image uploads excluded)
GET /rest/members/types
Return a list of possible member types (may differ by organization)
GET /rest/profiles/{profile_id}
Return a single profile and the corresponding member_id
GET /rest/members/{member_id}/vehicles
Return a list of vehicles for a single profile
Questions may be included in partial response by passing ?fields=questions in URL
GET /rest/members/{member_id}/vehicles/{vehicle_id}
Return a single vehicle for a single profile
Questions may be included in partial response by passing ?fields=questions in URL
GET/POST /rest/members/{member_id}/credits
Return or create a payment credit for a member
PUT/DELETE /rest/members/{member_id}/credits/{credit_id}
Update or delete a payment credit for a member
GET /rest/members/{member_id}/logbook
Return a list of log book entries about a single profile
GET /rest/logbooks/types
Return a list of log book entry types
POST /rest/logbooks
Create a new log book entry
GET/PUT/DELETE /rest/logbooks/{logbook_entry_id}
Return, update or delete a single log book entry
POST /rest/logbooks
Create a log book entry
GET/PUT/DELETE /rest/discounts
Return, update or delete discount codes
Search for a particular discount by passing ?code=YOURCODE in URL

HTTP Response Codes

When making a request, you can expect to receive one of the following HTTP status codes. All response codes are included in the HTTP Status response header. Possible status codes include:

Successful POST requests (e.g., to create a new resource) will return a status code of 201, include a Location header with the URI of the newly-created resource, and (usually) include the representation in the response.

Successful PUT requests (e.g., to update an existing resource) will return a status code of 200, and (usually) include the representation in the response.

Successful DELETE requests (e.g., to delete an existing resource) will return a status code of 204 (No Content) and no body.

Data Formats

If no format is specified, XML is the default, although these days we would recommend working with JSON instead. The easiest way to specify a format is to include a file "extension" in the URL. This unsettles the REST purists but is more pragmatic:

/rest/logbooks.json
/rest/discounts.xml
/rest/calendars/organization/{organization id}.rss

Most resources on api.motorsportreg.com also support the following values passed in an Accept header to specify the response format:

Name Format
application/vnd.pukkasoft+xml XML representation
application/vnd.pukkasoft+json JSON representation

Certain resources, notably calendars, also support the following:

Name Format
application/vnd.pukkasoft+rss RSS representation
application/vnd.pukkasoft+atom Atom representation
application/vnd.pukkasoft+calendar iCalendar/ics representation

Authorized Requests

All requests requiring authentication without valid credentials will be denied with a 401 Unauthorized response.

Basic Authentication

You can pass a username and password to most HTTP libraries like in this example with cURL

curl -u username:password https://api.motorsportreg.com/rest/resource/etc

Most HTTP libraries will handle Basic Authentication details automatically. To do it manually, include an Authorization header with a value like Basic credentials, in every request. The credentials string should be the Base64-encoded version of the string username:password. For example:

Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==

If your credentials are correct but you lack sufficient authorization, the request will be denied with a 403 Forbidden response.

Data Compression

Our REST API supports gzip/deflate compression for transmission. You can signal to our API you wish to receive compressed data by sending an additional header:

Header Value
Accept-Encodinggzip,deflate

Your software will need to decompress the data before using it (which may happen automatically). This is especially useful for the full events feed which can include more than 3000 events at a time and may be several megabytes uncompressed.

Testing and Debugging

The best tool to have in your arsenal is Firebug and Poster, two plugins for Firefox, that will let you inspect, modify and view requests sent from your browser to our API. If you prefer to develop from the command line, cURL is an HTTP client that is very flexible and is used in some of our examples below. We also suggest you look at the apigee developer console which lets you consume a REST API from a web browser with a lot of flexibility. Point it at our resources and you'll see the power quickly.

Example Interactions

Most developers use cURL to test and debug RESTful APIs. This is a simple command-line tool that you can use to get up to speed with our API.

To retrieve a representation, use the GET method. For example (using cURL's -X option to specify the request method):

curl -u userid:password -X GET https://api.motorsportreg.com/rest/calendars.json

Using an existing administrative account, pass in the X-Organization-Id header:

curl -u userid:password -H "X-Organization-Id: {your-id-here}" \
    -X GET https://api.motorsportreg.com/rest/calendars.json

To create a new resource, use the POST method on a collection. The body of the request should be an XML or JSON representation of the member resources. For example, (using cURL's -d option to provide a request body):

curl -u userid:password -d '<?xml version="1.0" encoding="UTF-8"?>XML Data' -X POST https://api.motorsportreg.com/rest/calendars.json

The PUT method changes existing resources. The body of the request should be an XML or JSON representation of the member resource. For example:

curl -u userid:password -d '{JSON Data}' -X PUT https://api.motorsportreg.com/rest/calendars/event_id.json

The DELETE method permanently removes a resource. For example:

curl -u userid:password -X DELETE https://api.motorsportreg.com/rest/calendars/event_id.json

Note that in some cases, deleting a resource will cascade to related resources. For example, deleting an event will also delete the attendees associated with the event.

Because not all HTTP libraries support the PUT and DELETE methods, they can be tunneled through POST by including a _method query parameter in the request, with a value of either PUT or DELETE:

curl -u userid:password -d '<?xml version="1.0" encoding="UTF-8"?>XML Data' -X POST https://api.motorsportreg.com/rest/calendars.xml?_method=PUT

PHP Samples

Here is a simple example of fetching the events as JSON using PHP and caching them in a file. You must have the cURL extension compiled in which is common in most builds of PHP. You can check by using phpinfo() which will include details about cURL if it is available.

Note: we have released free Wordpress and Joomla plugins for displaying an event calendar on your website. Learn more.
<?php
	// function handles requesting and caching the json
	function request_cache($url, $dest_file, $timeout, $flush = false)
	{
		if (!file_exists($dest_file) || filemtime($dest_file) < (time()-$timeout) || $flush === TRUE)
		{
			$ch = curl_init();
			curl_setopt($ch, CURLOPT_URL, $url);
			curl_setopt ($ch, CURLOPT_FOLLOWLOCATION, 1);
			curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
			curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 30);
			curl_setopt($ch, CURLOPT_TIMEOUT, 90);
			curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
			// set content-type and authorization headers
			$headers = array(
				"Accept: application/vnd.pukkasoft+json" // an alternative way of specifying content type, rather than .json or .xml in the URL
			);
			curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
			$data = curl_exec($ch);

			if (!curl_errno($ch) && curl_getinfo($ch, CURLINFO_HTTP_CODE) === 200)
			{
				$tmpf = tempnam('/tmp','motorsportreg-api-request');

				if (!$fp = fopen($tmpf, 'w'))
				{
					echo "Cannot open temporary file ($tmpf)";
					exit;
				}
				if (fwrite($fp, $data) === FALSE)
				{
					echo 'Failed to write data to file';
					exit;
				}
				fclose($fp);
				if (!rename($tmpf, $dest_file))
				{
					echo 'Failed to rename temporary file to cache file! This usually happens because PHP cannot write to the file: ' . $dest_file . '. Check permissions or consider placing the file in /tmp';
				}
			}
			elseif file_exists($dest_file)
			{
				// something went wrong, so give ourselves 30 minutes to try again and return existing cache
				touch($dest_file, strtotime("+30 minutes", filemtime($dest_file)));
				$data = file_get_contents($dest_file);
			}
			else
			{
				echo "Unable to initialize event feed on first run.";
				$mock = array('response' => array('events' => array()));
				$data = json_encode($mock);
			}

			curl_close($ch);

			return $data;
		}
		else
		{
			return file_get_contents($dest_file);
		}
	}

	// uri to data, cached for 6 hours at a time
	// if you receive "failed to rename temporary file..." errors, try changing the 2nd argument to /tmp/apimotorsportregcom.json
	$json = request_cache(URL_TO_THE_RESOURCE, './apimotorsportregcom.json', 21600);

	$data = json_decode($json, true);
	$events = $data["response"]["events"];

	// now display the events
	echo '<table>';
	if (count($events))
	{
		foreach($events as $event)
		{
			$dte = strtotime($event["start"]);
			echo '<tr><td>' . date("m/d/y", $dte) . '</td><td><a href="' . $event["detailuri"] . '">' . $event["venue"]["name"] . '</a></td><td>' . $event["venue"]["city"] . ', ' . $event["venue"]["region"] . '</td></tr>';
		}
	}
	else
	{
		echo '<tr><td colspan="3">No events matched your search!</td></tr>';
	}
	echo '</table>';

?>

To provide an Organization ID with authentication in PHP, you can add the following to your array of headers:


	$headers = array(
		"X-Organization-Id: {your-id-here}",
		"Authorization: Basic " . base64_encode('user:pass')
	);
	

Javascript Samples

We support returning data as JSON and JSONP. We posted some REST consumer examples in our blog and have included one below for creating a per-organization calendar list. This sample uses the popular Javascript library jQuery.


<html>
<head>
	<title>MotorsportReg.com API Test</title>
	<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
</head>
<body>

<script>
$(document).ready(function()
{
	$.getJSON('https://api.motorsportreg.com/rest/calendars/organization/{your-id-here}.jsonp?jsoncallback=?'
		,{
			dataType: "jsonp"
			,cacheBuster: new Date()
		 }
		,function(json)
		{
			var tbl = '<table>';
			$.each(json.response.events, function(i, evt)
			{
				tbl += '<tr>';
				tbl += '<td><a href="' + evt.detailuri + '">' + evt.name + '</a></td>';
				tbl += '<td>' + evt.type + '</td>';
				tbl += '<td>' + evt.venue.city + ', ' + evt.venue.region + '</td>';
				tbl += '<td>' + evt.start + '</td>';
				tbl += '<td>' + evt.end + '</td>';
				tbl += '<td>' + ((typeof(evt.registration.start) === 'undefined') ? '' : evt.registration.start) + '</td>';
				tbl += '</tr>';
			});

			tbl += '<' + '/table>';
			$('#msrCalendar').append(tbl);
		}
	);
});

</script>

<div id="msrCalendar"></div>

</body></html>
	

ColdFusion Samples

Here's an example using CFML:

<cfhttp username="user" password="password" url="https://api.motorsportreg.com/rest/calendars.json" method="get">
</cfhttp>

<-- dump the entire results -->
<cfdump var="#cfhttp#" label="Full Dump" />

<-- list just the event names -->
<cfset x = xmlSearch(xmlParse(cfhttp.FileContent), "/response/events/event/name/") />

<cfloop from="1" to="#arrayLen(x)#" index="ii">
	<cfoutput>#x[ii].xmlText#<br /></cfoutput>
</cfloop>

If you authenticate using your personal account, pass in the Organization ID as a header:

<cfhttp username="user" password="password" url="https://api.motorsportreg.com/rest/calendars.json" method="get">
	<cfhttpparam type="header" name="X-Organization-Id" value="{your-id-here}" />
</cfhttp>
	

IFRAME Sample

In certain cases, it's possible to ask our API to return the results as HTML such that they may be embedded into another site with one line of code. Currently this only works for the per-organization calendar because it does not require authentication but may be expanded in time. Never embed your API key in a public web page or code snippet!


<iframe src="https://api.motorsportreg.com/rest/calendars/organization/{your-id-here}.html" height="300" width="100%" style="border: 1px solid #ccc; margin: 10px 0;"></iframe>
	

RSS/Atom/iCalendar Formats

In certain cases, it's possible to ask our API to return the results as a common format such as RSS, Atom or iCalendar. These feeds are easily digested by many applications because of their ubiquity. This example shows the per-club (unauthenticated) calendar feed for each of the three types. The api.motorsportreg.com feeds are NOT designed for end-user consumption. They may change over time or move locations so we recommend that you retrieve the data, cache it and serve it from your own application.


https://api.motorsportreg.com/rest/calendars/organization/{your-id-here}.rss  (RSS 2.0)
https://api.motorsportreg.com/rest/calendars/organization/{your-id-here}.atom (Atom 1.0)
https://api.motorsportreg.com/rest/calendars/organization/{your-id-here}.ics  (iCalendar)