The Smashrun API

Registration

It's easy to get access to the API. You can build something for yourself, or something that helps other users, just be considerate and don't hammer our poor little API for no good reason. If something feels inefficient, ask us for help and we'll do our best to make it work better.

Getting access

There are 2 ways of accessing the Smashrun API:

  1. You can request a key for a multi-user application you're writing.
  2. You can use user level authentications to access your own data as described below (it's super easy).

User Level Authentication

It's very simple. All you need to do is use the client id: "client" along with the implicit OAuth flow.
Note: If you're accessing the API from a mobile app. You should request an API key instead.

Advantages

Disadvantages

OAuth 2.0 Authentication and Authorization

Smashrun API uses OAuth 2.0 to provide access tokens to protected resources. Client applications can make use of either an access code flow for web server applications or an implicit flow for mobile apps and JavaScript applications.

Once you have an access token you can authorize requests to the the API by either including it as either a url parameter or an authorization header in any subsequent request.

Quick start for OAuth experts

If you're experienced with the OAuth standard then all you really need to know are the the specifics of our implementation.

Full instructions

The basic steps making use of the Smashrun API (or any OAuth enabled API) go like this:

  1. Obtain client credentials. In the future, you'll probably be able to do this by filling out a form but, for now, this means sending us an email asking for some.
  2. Obtain an access token. An access token is like a username and password rolled into one. However, it has several distinct advantages:
    • You never have to store or handle the password yourself.
      That means if you get hacked, you don't take us down with you.
    • An access token is associated with a set of permissions. If you only need to read runs, you can ask for permission to do only that. This means you can't get accused of messing up something you never had access to in the first place.
    • Users can easily revoke access tokens. This means if you get hacked and your token starts getting used for comment spam. The user can solve the problem themselves before you even noticed it happened.
    You'll notice there's a common theme here. OAuth means less responsibility. When it comes to security, the less responsibility, the more awesome.
  3. Send the access token with each request. You can send the access token as either a query string parameter (access_token) or in the authorization header. The API checks the permission grants of the token and the expiration of the token, and acts upon your API request.
  4. Refresh the access token when it expires. Our access tokens last 12 weeks. If you're using the code based flow, then you'll be provided with a refresh token. You can store this refresh token and use it get a new access token without prompting the user to re-authenticate.

This all sounds pretty straightforward right? The place where people commonly get hung up is that 2nd step Obtain an access token.

Obtaining an access token

This is the heart of the whole OAuth thing. It's actually pretty straightforward, but if you're new to OAuth, then it might seem a lot more complicated than it actually is. The reason why is because there are a lot of different types of OAuth, and there are different possible flows for each version. But, rest assured, once you've got that nailed down, the rest is cake walk (UK/piece of piss).

Smashrun like many new APIs uses OAuth 2.0 and supports the code and implicit based flows. The other common standards you might hear about are OAuth 1.0 and 1.0a. These aren't worse standards in the way you might think version 2 is automatically better than version 1. They're just different. OAuth 2 is a lot easier to work with and it's also a little less secure. Twitter uses OAuth 1.0a, but Facebook uses OAuth 2.0. The big difference is that OAuth 2 requires signing requests, and signing requests offers additional security, but it's really easy to screw up.

In a nutshell OAuth 2.0 flow looks like this: first you pass the user from your app/website to an API provider's website. The user then signs in and approves a certain set of permissions that you included in the link when you passed them over. Once they sign into the provider (i.e. Smashrun), the provider redirects the user back to your app/website.

This is where the code and implicit flows start to deviate...

If you're using the code based flow, then when you the user gets redirected back, the redirect url includes a code, and if you're using the implicit flow (aka token flow) the redirect contains a token instead. That token is like gold. It's all you need to make requests to the API on a user's behalf for as long as you need to, well that is, until it expires.

However, if you're using the code based flow then you still need to take one more step. The code you got back needs to be exchanged for that all important token. You do that via a simple POST request on the server side passing the client_id, client_secret, code, and grant_type=authorization_code.

Note:

The response from that request includes not only a token but, as an added bonus because you went through the effort of doing the 2 step authorization code flow, you also get a refresh token. A refresh token is a pretty handy thing because you can use use it to get a new auth token anytime you need one, no need to have the user sign in again.

If that still seems a bit confusing. Check out this blog post. It's one of the best explanations of the OAuth 2.0 flow and terminology that you'll find.

OAuth in practice

Here's the beautiful thing about a standardized specification: one implementation is pretty much the same as the next. You could, for example, just read Google's OAuth 2.0 Documentation and do everything they say, but replace their OAuth urls with ours and everything should pretty much work just fine. Their docs are really comprehensive and professional, because, well they're Google.

Implicit flow

This flow is little less secure and you don't get a refresh token, but on the other hand it's just one step and you don't need any server side code.

https://secure.smashrun.com/oauth2/authenticate?client_id=your_client_id&scope=scope_list&response_type=token&state=some_state&redirect_uri=http://yoursite.com/callback
client_id
We give this to you when you register your app. Note the conspicuous absence of a client secret. Since this flow is designed to run on code that a user controls, you can't very well give the user your secret, because well, it's supposed to be a secret.
scope
This is a space separated list of permissions. We'll ask the user if she wants to allow your app to do those things and, if they say yes, the auth token we return will be allowed to. Currently, the only options are read_activity and write_activity. If you leave this field blank, we'll ask for permission to do everything.
response_type
This is just "token" because this flow returns a token (rather than a code).
state
Just any string you want (or no string at all). When we make the callback, we'll include this same state field, and you can use if for your own purposes if it makes your life easier...or not, it's up to you.
redirect_uri
This is an endpoint on your site. We'll make a GET request to it after the user authorizes your app. Our call back to your site will look like this:
http(s)://yoursite.com/callback#access_token=the_access_token&token_type=bearer&expires_in=large_number_of_seconds
access_token
Your golden ticket. Take it and store it somewhere safe to use with each API call.
token_type
Since you asked for a token, that's what we gave you. There are many different types of OAuth token, but the only ones we return are bearer tokens. So, you can safely just ignore this.
expires_in
The number of seconds until this token expires. It'll be 12 weeks of seconds. When the token expires, you'll need to get a new one. Although, you should be ready for the token to expire at any time, because a user can revoke it with a click of a button.

Code flow

The code flow is very similar to the implicit flow, but there's another step at the end. Instead of getting the token directly, you get a code instead. And then send us that code back along with your client credentials (client_id and client_secret) and we send you the auth token as well as a refresh token you can use to get a new auth token anytime you want.

On the one hand, there's a second step, but on the other hand you get to authenticate your client and you get a refresh token so you don't have to bother with the whole process again.

Step 1
https://secure.smashrun.com/oauth2/authenticate?client_id=your_client_id&scope=scope_list&redirect_uri=http://yoursite.com/callback&response_type=code&state=some_state

Look familiar? This is almost the same url as the one we used in the 1 step implicit flow. The only difference is that, this time, the response_type is set to code instead of token.

client_id
We give this to you when you register your app. It's just a unique identifier for your app.
scope
This is a space separated list of permissions. We'll ask the user if she wants to allow your app to do those things and, if they say yes, the auth token we return will be allowed to. Currently, the only options are read_activity and write_activity. If you leave this field blank, we'll ask for permission to do everything.
response_type
This is just "code" because this flow returns a code that you exchange for a token in Step 2.
state
Just any string you want (or no string at all). When we make the callback we'll include this same state field, and you can use if for your own purposes if it makes your life easier...or not, it's up to you.
redirect_uri
This is an endpoint on your site. We'll make a GET request to it after the user authorizes your app.

But, what if you don't have a webserver you can depend on? For example, what if you're building a desktop app that your user will run on their computer? In that case, you can send a special redirect url that looks like this: redirect_uri=urn:ietf:wg:oauth:2.0:oob (except you'll want to url encode it, of course).

If you send us this code instead of a uri, then there's a special behavior. Once the user authorizes your app, the authorization code will be written to the title of the web browser. You should be able to read this title from your application and close the window, but, just in case you can't, the user will also be prompted to copy the code and paste it into your app (probably into a box that you create that says "put code here").

But, in the usual case, you'll just send us a normal url and our callback to your site will look like this:
http(s)://yoursite.com/callback?access_token=the_access_token&token_type=bearer&expires_in=large_number_of_seconds&state=some_state
code
This is the authorization code you asked for. In step 2 you'll exchange this for a token.
state
This is just the string you passed us. We just give it back to you.
expires_in
This is how long you have before this code expires. It won't be long, so you should get busy on step 2 right away. Part of the security of the code step is that these codes aren't valid for very long, so if someone tries to exploit one, they've got a pretty narrow window.
Step 2

Now that you've got your authorization code, the next step is to send that code, along with the client id and client secret we gave you, so that you can get an auth token and a refresh token.

Unlike the first step, this is one is a POST request from your server that returns a JSON response.

POST: https://secure.smashrun.com/oauth2/token
Parameters: grant_type=authorization_code&code=your_code&client_id=your_client_id&client_secret=your_client_secret
grant_type
This is just "authorization_code". It's asking "What makes you think you can have an access token?". And you're saying "Well, I have an authorization code." You could also say "refresh_token" but more on that later....
code
This is the code you got from step 1.
state
This is just the string you passed us. We just give it back to you.
client_id
We give this to you when you register your app. It's just a unique identifier for your app.
client_secret
We give this to you when you register your app. It's your app's password, and it's actually the only reason this whole 2 step code flow exists. You could have passed the secret in step 1 and skipped this whole 2 step malarkey but, of course, then it wouldn't be a secret anymore. Because step 1 is a GET redirect for the user, the user could just look at the url, copy your secret, and send it to the NSA. (Ok...the NSA probably already has secret already, so maybe not the best example).

The response comes back as JSON

{   "access_token":"oAfoij284AADSfdoijaf",
    "expires_in":3920,
    "token_type":"Bearer",
    "state": "some_state",
    "refresh_token": "94ddgJaad4hfdGPS52" }

You get an access token that you can use to make requests to the Smashrun API, and you also get a refresh_token.

Refreshing the access token

When a user's access token expires, getting a new one is simple. All you need to do is make the same call you made to exchange the authorization code for a token, only this time, you're exchanging a refresh token.

POST: https://secure.smashrun.com/oauth2/token
Parameters: grant_type=refresh_token&refresh_token=your_refresh_token&client_id=your_client_id&client_secret=your_client_secret

The response will be JSON and will look almost exactly like the one you got when you exchanged the code

{   "access_token":"8fffij184gdD5fdo7daa",
    "expires_in":3920,
    "token_type":"Bearer",
    "state": "some_state"

Just store this new access token, and repeat the process whenever the token expires. The access token will last 12 weeks, and the refresh token will last forever, unless a user explicitly disconnects the app.

Checking the access token

You can easily check the status and validity of an access token at any time.

GET: https://api.smashrun.com/v1/auth/{token}

Example response:

{   "scopes":["read_activity","write_activity"],
    "token":"8G12414afgggHgAyCUNwT80CmvO2hzcw5DzY73Y0",
    "client_id":"yourclientid",
    "expires_in":7243186  }

Revoking access

You can also revoke your own access by deleting a single token, or delete all access tokens for a given client id

To revoke one token:
DELETE: https://api.smashrun.com/v1/auth/{token}
To revoke all access:
DELETE: https://api.smashrun.com/v1/auth

Sending runs

You can POST GPX, TCX, or JSON formatted data to the activities endpoint. The API will identify the content type and import it into the authenticated user's account.

POST: https://api.smashrun.com/v1/my/activities/
Parameters: Accepts GPX/TCX or activity JSON (detailed below)

If you want to keep your transactions lightweight and avoid data issues (and the countless support problems that come with them), you'll almost certainly want to post JSON activity objects as outlined below. Yes, I know. It's yet another format. But don't think of it as another format. Think of it as little packets of run data, overflowing with accurate measurements and devoid of ambiguity. Think of it as an investment in the future. Visualize user satisfaction in a nice tidy JSON format.

The following operations are supported for activities: GET, POST (insert), PUT (update), DELETE (flag as deleted), and PATCH (update selected fields)
Updates are performed against the complete object, so you must include the same full JSON object you would with a POST. The only difference is the necessary inclusion of an activityId field that matches an existing id of a run owned by the authenticated user. Currently PATCH only supports the notes, duration, distance, and startDateTimeLocal fields of the activity object. If you have a requirement to update other fields please send us an email.

Inserts via POST are processed through a de-duplication algorithm. If a run matches an existing run in the system the following logic is applied:

At present, you can only overwrite a run that was originally created by your application. If you send a run that is identical to a run that already exists in the application from another source then that run will simply dupe. However, if you send a run that matches an existing run created by your app then that run will be overwritten with the latest run.

The Activity JSON Object

The JSON activity object is a highly compressed format designed specifically for fitness activity (running in particular). It attempts to address some of the failings of standard XML formats by reducing ambiguity in the way data series are codified. It consists of a flat object containing aggregate summary info for an activity along with a records object, which contains detailed time series data. Where time series data is present, the summary data will be recalculated and any provided values overwritten in order to keep consistency between detail and summary views.

Fields

startDateTimeLocal
Required

The time at the location where the run occurred (including timezone offset).
Must be ISO 8601. For example: 2014-07-01T09:28:56.321-10:00

distance
Required
* If not present in recordings

The distance of the activity in kilometers (decimal).
Activities returned from the API may have summary distances that don't match the last distance in the recordings. This can be the result of either runs that have a manual edit by the user, or runs where the source (usually TCX or API) has supplied a different summary distance than the last individual recordings. One common cause of this is incorrectly flagged pause.

duration
Required
* If not present in recordings

The duration of the activity in round seconds, excluding any pauses (integer).
Like distance, the summary data may conflict with the recording. However, when saving a run from a direct source (i.e. you're the app that recorded it) you should ensure that the last value in the recordings array for duration (and distance) corresponds exactly to the summary value supplied here.

activityType

The type of activity. For now, only "running" is accepted. At his time all other activity types will be rejected and shouldn't be sent.

notes

A description of the run entered by the user. (Max 800 chars)

cadenceAverage

Average steps per minute (one leg).
Typical values: 60 - 120. Should be a weighted average by duration between recordings.

cadenceMin

Minimum steps per minute (one leg).

cadenceMax

Maximum steps per minute (one leg).

heartRateAverage

Average Beats Per Minute
Typical values: 60 - 220. Should be a weighted average by duration.

heartRateMax

Maximum heart rate beats per minute

heartRateMin

Minimum heart rate beats per minute

bodyWeight

The weight in kilograms of the athlete on the date of the run

howFelt

One of the following string values: [Unstoppable, great, soso, tired, injured]

terrain

One of the following string values: [road, trail, track, treadmill, beach, snowpack]

temperature

Average temperature in round degrees Celsius for the duration of the run.

weatherType

One of the following string values: [indoor, clear, cloudy, rain, storm, snow]

humidity

Percentage relative humidity expressed as a round integer (0-100).

windSpeed

Average wind speed over the duration of run expressed as a round integer in kph.

externalDeviceType

The type of device on which the software which made the recordings was running. Valid values include: Google, Apple, RIM, Microsoft, Symbian, or Other.

externalId

An id used internally by your app to uniquely identify this run. If there's an existing run in Smashrun sent to a user's account by your app with the same id then it will be considered an update to an existing run rather than a new run.

externalAppVersion

The current version of the software on which the run was recorded.

pauseIndexes

An array containing the 0 based indexes of the recording values immediately following any pauses. The duration of the pause is equal to the difference between the clock recordingValue at this index, and the preceding index.

laps

An array of name value pairs specifying the lap and the type of effort. Units are meters and seconds.
For most accurate display, send distance based laps by distance and duration based laps as time. If both duration and distance are sent, duration will be used.

Laptype

general
Not the work or recovery part of an interval, or a warmup, or cooldown.
work
The work period of an interval
recovery
The recovery period of an interval
warmup
Warmup before main period of work
cooldown
Cooldown after main period of work

Examples:

laps: [{lapType: "recovery", "endTime": 60,}, {lapType: "interval", "endDuration": 120}]
laps: [{lapType: "recovery", "endDistance": 250,}, {lapType: "interval", "endDistance": 500}]

songs

An array of songs that were playing during the run. Song start and end time is specified by the clock time (not duration) from the start of the run. BPM is an optional field for the tempo of the song in beats per minute. It can be included if known. Example:

"songs": [{ "album": "13 Songs", "artist": "Fugazi", "song": "Waiting Room", "BPM": 155, "startClock": 0, "endClock": 150 },
{ "album": "The Suburbs", "artist": "Arcade Fire", "song": "We Used to Wait", "BPM": 145,"startClock": 151, "endClock": 400 } ]

heart rate recovery

These are heart rate values recorded after the end of a run. They are specified as an array of heart rate recordings consisting of a heartRate(bpm) and duration(in secs). You may include any frequency of data available from 1 recording per second to a single recording at a set interval of 30 seconds, 1 minute, or 2 minutes for example. Naturally the more data that is sent the better the end user experience, because we can detail the arc of the recovery curve. Example:

"heartRateRecovery": [
   { "duration": 0, "heartRate": 190 },
   { "duration": 5, "heartRate": 180 },
   { "duration": 10, "heartRate": 168 }
....

Recordings Arrays

Recordings is an array with each element containing another array of floating point values. It's a collection of various time series data recorded at set intervals. The time series can be added in any order. However, when you include a data series in the recordings object, you also must include its name in the recordingKeys array. We use the position of the name in recordingKeys to find the position of the data series you added.

Available time series

The only required series are Duration and (one or both of Distance and Latitude/Longitude). If the latitude and longitude series are included, but distance series is not then distance will be calculated automatically. However, since there are a variety of techniques for calculating distance it is best to include both distance and lat/lon if both are available.

Here's the full list of available data series. If there's an additional series you think might be useful, shoot us an email and we'll consider adding it.

recordingKeys

clock
seconds since the start of the activity
This can either be round seconds or a floating point value containing milliseconds. Clock time is a required field. Note: If there are any pauses in the activity either duration or pauseIndexes MUST be supplied.
duration
seconds excluding any pauses (pauses will be inferred from differences between the clock and duration)
This can either be round seconds or a floating point value containing milliseconds.
Note: If the pauseIndex array is supplied, or if the run contains no pauses, it is not necessary to include the duration data series.
distance
the distance travelled in kilometers
if latitude and longitude are included, but distance is not, then distance will be calculated
latitude
if not specified, then distance must be included
longitude
if not specified, then distance must be included
elevation
the elevation specified in meters
heartRate
specified in bpm
cadence
steps per minute. (one leg) Typical values: 60 - 120
power
power in watts. Typical values: 100 - 600
powerForm
form power in watts. Typical values: 20 - 200 (Stryd)
temperature
the temperature in Celsius
gpsAccuracy
the horizontal accuracy in meters of the the gps recording (may include gpsAccuracy or satellites but not both)
satellites
the number of satellites used to establish the location (may include gpsAccuracy or satellites but not both)

recordingValues

An array of arrays of floating point values containing the recordings specified in recordingKeys

Tips and Guidelines

Why distance recordings might not match your calculations from GPS coordinates

It's important to realize that the GPS coordinates recorded for a given run are, to some degree, independent of the distance and, therefore, the speed of that run in aggregate or at any point in time. This is, of course, a bit unexpected. You might easily expect that the GPS coordinates are the verifiable truth of a given run, and that distance and pace can simply and easily be derived from these coordinates by applying certain standard equations, but you'd be wrong. Why?

Fitness devices interpret their own GPS readings. A given device knows something about it's own failings. In the most blunt type of interpretation, a given device might know that its GPS chip set overestimates distance by 10-15% on average. To counteract this, it might simply lob off 12% from the distance of every recording calculated compared to what's calculated from the raw GPS points. Or a device might have access to additional information. It might know from a footpod or an accelerometer that you were still moving at a steady pace, even though the GPS points might say otherwise.

There is no one accepted GPS coordinate to distance calculation. There are dozens of ways to calculate distance from GPS coordinates. Some methods are verifiably better than others, some take into account altitude gained, some discard that data, some use a model of the earth as a sphere, others may use one of many different reference ellipsoids. There's no way of knowing which technique a given device employs.

What all this means in practice is that, if you're saving activities, you should always include a distance time series in addition to the GPS coordinates. And if you're reading activity data, you should always make use of the distance time series rather than trying to apply any type of calculations to the GPS coordinates. And it follows that if no distance time series is available, then it will be very difficult to match the metrics that a given device reports.

FIT Files

FIT files are a preferred to GPX or TCX files. They provide more consistent structured data, and since thay are a binary format they are also incredibly compact. To upload a FIT file through the Smashrun API all you need to do is execute a multipart/form-data POST containing the FIT data.

Smashrun makes use of the following FIT messages and fields:

  • file_id
  • product
  • time_created
  • session
  • sport
  • total_elapsed_time
  • total_timer_time
  • total_distance
  • lap
  • timestamp
  • total_elapsed_time
  • total_timer_time
  • total_distance
  • record
  • timestamp
  • position_lat
  • position_long
  • altitude
  • heart_rate
  • cadence
  • distance
  • power
  • temperature
  • event
  • timestamp
  • type

Guidelines for TCX Files

The TCX file was specifically designed for fitness data. There are established ways of including additional data series, like heart rate or cadence. And, if you follow all the guidelines below, the file should import exactly as it was recorded and everyone will love you dearly. That said, the number of TCX files we receive which follow all of the import specifications below is vanishingly close to zero. The reason for this is simple: it's hard. And it's not really documented anywhere (except here, now, sort of).

General

Pauses

Laps

Extended data

Guidelines for GPX Files

GPX files contain timestamps and location information. Any additional data needs to be marked up and included with proprietary extensions. In this way, it is much leaner than TCX files, but it is also poorly suited to fitness data. Runs on treadmills can't be represented because they have no GPS points, but distance is travelled. Additionally, since there are many different methods for calculating distance from latitude and longitude points, distances can vary depending on the calculation used. In short, you should really use the Activity JSON object instead. But, let's say there's a good reason not to. Here's what you need to remember:

FIT Files

In addition to GPX and TCX files you can also post FIT files directly to Smashrun via the API. This is done by simply posting the file to the /activities endpoint as multipart/form-data.

Getting runs

You can get the full details of any run for the authenticated user's account by passing a run id to the activities endpoint. This id maps to the main Smashrun site like this http://smashrun.com/chris.lukic/run/1236343

https://api.smashrun.com/v1/chris.lukic/activities/1236343
Returns an activityDetail object for the authenticated user

To get an array of run summaries you can can simply make a request to activities with no arguments

https://api.smashrun.com/v1/chris.lukic/activities
Returns an array of activitySummary objects in descending order of the most recent activity date.

But, because that can frequently return an excess of data, it's more likely you'll want to return only a specific subset of runs. To do this you can use the activities/search endpoint.

You can use search to page data like so...

https://api.smashrun.com/v1/chris.lukic/activities/search?page=5&count=10
This returns the 6th page (starting with 0 as all good coders count) of 10 run summaries.

You can use search to grab only new and updated runs from a date. This might be handy for syncing...

https://api.smashrun.com/v1/chris.lukic/activities/search?fromDateUTC=1405439180
This returns all runs that have been added or modified since the UTC timestamp 2014-07-15T15:46:20Z

Or, if you want to minimize bandwidth and the load on our tiny servers, maybe you just want to grab run ids.

https://api.smashrun.com/v1/chris.lukic/activities/search/ids
Returns the first 100 smashrun run ids.

Or if you need a bit a bit more than just ids adding briefs returns a set of the basic info needed to describe a run as unique.

https://api.smashrun.com/v1/chris.lukic/activities/search/briefs
Returns an array of json objects containing the runId, startTime, distance (in kilometers), and duration

On the other hand you may need an extended set of run data, for example, if you need all of the data related to the badge calculations for a run. In the case you can request extended run summaries.

https://api.smashrun.com/v1/chris.lukic/activities/search/extended
In addition to the complete field list returned by the full summary. Specifying extended results in the return of the following additional fields:
speedVariability
% variation in pace at measured 10 sec intervals
isCooperTest
Is this a maximal effort Cooper test
sunriseLocal
The time of sunrise
sunsetLocal
The time of sunset
moonPhase
Phase of the moon. 0.50 is full, 1.0 is new
countryCode
2 digit ISO 3166-1 alpha-2 country code
country
Full name of the country in the local language
city
City in local language
state
State/region in local language
elevationGain
Sum of all elevation gains
elevationLoss
Sum of all elevation losses
elevationAscent
Minimum elevation to maximum following elevation
elevationDescent
Maximum elevation to minimum following elevation
elevationMax
Highest elevation
elevationMin
Lowest elevation
elevationNet
Net change in elevation. Should be ~0 for round trip runs. (excluding pause elevation)
powerMax
Maximum power in watts
powerAverage
Maximum power in watts

Getting notables

Notables provide context for each run by comparing one run against all runs that proceeded it over a variety of dimensions. Each notable is composed of two or three pieces of information.

When you request a list of notables for a run. The object returned contains the text of the notable a well as the supporting information. Note: The text is currently always returned in English (send us an email if this is an issue)

Notable object:

description
English text description of the notable
reasonCode
A code for the type of notable
periodMonths
Period of the notable in months. Possible values [1, 3, 6, 12, 99(ever)]
supportingValue
For reason codes: speedmi/speedkm/days. Supporting value is the reference distance or # of days
isImperial
Is this notable based off an imperial reference measurement
value
The relevant value for this activity that is notable
periodValue
The best value in the reference period before this activity

To retrieve notables

https://api.smashrun.com/v1/chris.lukic/activities/344732/notables
Return an array of all notables for activity id 344732

Getting goals

Goals can be retrieved by passing either a year only or a year/month combination. A goal can include a textual description, such as "Improve my running economy this year", or they may include a goal distance, for example 1,000 km. Or they can include both items. You can retrieve goals by passing either a year, or year/month combination. January = 1, so requesting /goals/2019/1 will return the goal set for January.

Note: If no goal is set for a given period the response from the API will be null.

Goal object:

goalText
Unicode description of the goal (may be null)
goalKilometers
Goal distance (may be null)
kilometers
Total distance run so far during the goal period

https://api.smashrun.com/v1/chris.lukic/goals/2019
Return the goal set for 2019, and progress so far towards that goal.

Getting stats

The values retrieved include many of the aggregate metrics displayed on the Smashrun home page. These metrics can be returned for All Time (no arguments), one year(year only), and one month views (specify both year and month).

Stats object:

runCount
Number of runs
totalDistance
Kilometer total of runs
averageSpeed
Average speed weighted by distance km/hr
averagePace
Pace in string format min/km
longestRun
Longest run km
longestRunWhen
Date of the longest run (in timezone it occurred)
longestBreakBetweenRuns
Most consecutive days without a run
longestBreakBetweenRunsDate
Local date when break began
longestStreak
Most consecutive days run
longestStreakDate
Local date when streak began
daysRunAM
Count of unique days run before 12pm
daysRunPM
Count of unique days run after 12pm
daysRunBoth
Count of unique days with runs both before and after 12pm
averageDistancePerDay
Average distance per day km
averageRunLength
Average run kilometers
averageDaysRunPerWeek
Unique days run / days * 7
mostOftenRunOnDay
Most frequent run day of week
mostOftenRunOnCount
Count of runs on day
mostOftenRunOnAverageDistance
Avg. distance on day
leastOftenRunOnDay
Least frequent run day
leastOftenRunOnCount
Count of runs on day
leastOftenRunOnAverageDistance
Avg. distance on day

https://api.smashrun.com/v1/chris.lukic/stats/2016/1
Return some of the stats displayed on the Smashrun overview page for January 2016

Getting run splits

You can return the speed and and average heart rate (if available) at 1 mile or 1 kilometer splits.

To retrieve 1 km splits for a run

https://api.smashrun.com/v1/chris.lukic/activities/2328346/splits/km
Return all kilometer splits of activity id 6482589

To retrieve 1 mile splits for a run

https://api.smashrun.com/v1/chris.lukic/activities/2328346/splits/mi
Return all mile splits of activity id 6482589

Updating notes for a run

You can update the description field for a run by using the http method PATCH and passing an object with a notes field.

Example: {notes: "This interval session was the best one yet! 💪"}

Getting & saving weight

You can save or retrieve information about an athlete through the body endpoint.

https://api.smashrun.com/v1/chris.lukic/body/weight
Return a collection of all historical weight (in kg) and measurement dates for the authenticated user.
https://api.smashrun.com/v1/chris.lukic/body/weight/latest
Returns only the most recent weight recording and the date of that recording for the authenticated user.

To update a user's weight you can post a json object containing the attribute "weightInKilograms" and optionally "date". If date isn't included today's date will be used.

Example: {weightInKilograms: 90, "date": "2015-04-01"}

Getting information about a user

You can save or retrieve information about the user associated with the current bearer token through the userinfo endpoint. This provides useful information such as configuration options (kg/lbs, mi/km, etc), but it can also be used to track when a users run information has changed by checking the property: dateTimeUTCOfLastRunDataUpdated.

https://api.smashrun.com/v1/chris.lukic/userinfo
Return all provided metrics about the specified user (In the normal case you would simply call /my/userinfo)

id

A permanent unique id for the user. At present this is always a 64 bit integer, but you should store it as a string.

firstName

The first name recorded by the user

lastName

The last name recorded by the user

username

The username portion of the user's smashrun url http://smashrun.com/chris.lukic

dateTimeUTCOfLastRun

UTC timestamp of when the user's last run started

dateTimeUTCOfLastRunDataUpdated

UTC timestamp of the last time the user updated runs to smashrun from any source

unitDistance

One of either 'k' for kilometers or 'm' for miles

unitWeight

One of either 'k' for kilograms or 'p' for pounds

unitHeight

One of either 'm' for meters or 'f' for feet

unitTemperature

One of either 'c' for Celsius or 'f' for Fahrenheit

languageCode

Two or four letter ISO code for the user's configured language

hemisphere

'n' if the user last logged in from a location North of the equator, 's' for South of the equator

restingHeartRate

If configured returns the current resting heart rate of the user

maxHeartRate

If configured returns the current max heart rate of the user

followerCount

The number of users currently following this user

followingCount

The number of users that this user is following

timeZoneOffsetMinutes

The user's timezone offset in minutes (excluding the effects of Daylight savings time)

timeZonePlusDSTOffsetMinutes

The user's timezone offset in minutes (with daylight savings time included)

registrationDateUTC

The user's registration data (the date you can first start earning badges)

proBadgeDateUTC

If pro then, the user's pro badge earn date (the date they can start earning pro badges)

Something to keep in mind

Smashrun is a small company run by its 3 founders. Our love of running may be big, but our servers are small, so please be considerate when calling our API. If you're in doubt whether something's a good idea just shoot us an email before implementing it. We'll do our best to get back to you as soon as we can (usually this means immediately, but sometimes we're all out running). We might be able to make adjustments, add a new interface for you, or offer suggestions to help things work more efficiently.