Best Practices
chevron down
 

Web API Best Practices

Using Tokens Effectively

When a Fitbit user consents through the Authorization Code Grant Flow, the application is provided with an active access token and refresh token. The access token and refresh token are unique to each user and associated with only the client ID in which they were created. The access token grants permission to access the Fitbit user data. This token has a default lifespan of 8 hours before becoming invalid. For the application to maintain their consent, the refresh token should be used when the access token expires to get a new access token/refresh token pair. The refresh token never expires, but can only be used once. After the refresh token has been used, it becomes invalid and the application has to use a new refresh token.

A user may have multiple active access tokens for a single application. However, each user will only have one active refresh token. When the access token expires, the application can use the active refresh token to obtain a new access token and refresh token pair. Once the refresh token is used, it becomes invalid and is replaced by the new active refresh token. Your application should store both the access token and refresh token and correctly maintain which tokens are active.

Note: If a client makes identical refresh token requests, the Fitbit Web API will return the same response unless the newly issued access or refresh token is used in this interval. This is to assist applications unable to coordinate the refresh token flow in the multi-threaded processes or multi-servers world.

Examples

Suppose the application is only using a single access token per user.
Then the token exchange would resemble

  • Call /oauth2/token (grant_type=authorization_code) to create access_token_1 and refresh_token_1
  • When access_token_1 expires, call /oauth2/token (grant_type=refresh_token) specifying refresh_token_1 to retrieve the new access_token_2 and refresh_token_2. refresh_token_1 becomes invalid.
  • When access_token_2 expires, call /oauth2/token (grant_type= refresh_token) specifying refresh_token_2 to retrieve the new access_token_3 and refresh_token_3. refresh_token_2 becomes invalid.

If the application is using multiple access tokens per user, then the token exchange would resemble

  • Call /oauth2/token (grant_type=authorization_code) to create access_token_1 with refresh_token_1
  • Call /oauth2/token (grant_type=refresh_token) to create access_token_2 and refresh_token_2. access_token_1 is valid until it expires. refresh_token_1 becomes invalid.
  • Call /oauth2/token (grant_type=refresh_token) to create access_token_3 and refresh_token_3. access_token_1 and access_token_2 are valid until they expire. refresh_token_2 becomes invalid.

See Troubleshooting Guide for common refresh token errors.

Manually Entered Data

People usually ask how to determine if Fitbit users manually enter various data. Not all data has a clear tag to state where the source of the data is from. Here are some guidelines you can follow to determining where the source of the data was.

Manually Recorded Activity Data

  • Manually entered workouts will be recorded as an “activity”.
  • The Get Activity Log List endpoint returns the list of all recorded activities. Manually entered activities (i.e. step and workouts) will return “logType=manual”.

    For scenarios where manually entered steps should not be considered for a wellness program, the Get Activity Log List endpoint, /1/user/-/activities/list.json, will provide the number of steps recorded for each manually entered activity. Steps from these activities can be subtracted from the total number of steps displayed in the Get Daily Activity Summary endpoint.

Tracker Recorded Activity Data

  • Activities Time Series endpoints support the resource “tracker” to display data that comes directly from a Fitbit device. You can receive calories, steps, distance, floors, elevation, minutesSednetary, minutesLightlyActive, minutesFairlyActive, minutesVeryActive and activityCalories.
  • The Get Activity Log List endpoint will return “logType=tracker | mobile_run | auto_detected” if the data was recorded activity or SmartTrack detection.

3rd Party Recorded Activity Data

  • The Get Activity Log List endpoint will return “logType= {3rd party application name}” if the data was recorded by a 3rd party application.

Querying Historical Data

Once a user consents to share their data with an application, that application has the ability to query the user’s historical data. Querying historical data is still governed by the user rate limit. Fitbit provides time series endpoints to help reduce the number of API calls made by the application. Each time series endpoint has a maximum amount of data that can be returned in a single request, from 30 days to several months. See the time series endpoints for more information. Depending on the amount of historical data the application needs, it may take several hours to retrieve all of the data. Developers should keep this in mind when designing their application.

Reducing API Execution

Time Series vs Daily Summary

Most applications execute the activity daily summary endpoint to get the activity data processed for a single day. When an application needs to query data for multiple days, the time series endpoint can be helpful to reduce the number of API calls.

For example, an application needs 1 month of activity data. Using the Get Daily Activity Summary endpoint to collect 1 month of data results in 30 executions. Using the time series endpoint can reduce the number of endpoint executions from 30 to 12 (1 for each resource and the activity log list).

Retrieving GPS data with HR Zones

Applications that require GPS and heart rate zones should not execute the Get Activity TCX endpoint for every exercise recorded. Instead, use the Get Activity Log List endpoint looking for the exercises whose logType is "Tracker" The JSON response will contain the "source" object displaying the name of the device and the features that were used for the exercise (such as “Heartrate” and “GPS”). Record the logId of the activities with those features, then execute the Get Activity TCX endpoint, specifying the logId, to pull the relevant data.

NOTE: GPS and heart rate data are only available for exercises recorded on a GPS and heart rate enabled Fitbit device.

Multi-Device Overview

Fitbit allows the concurrent use of multiple activity tracker devices and scales. Fitbit automatically detects when a person switches from one tracker to another throughout their day or week with no buttons to push on the device or the app. This unified data is then presented to users.

TIP: Any device that uses the Fitbit App Gallery may only have one device connected to the account at a time. The devices that apply are Charge 3 and above, Ionic, Inspire Series, Versa Series and Sense.

Considerations

  • Data provided through the Fitbit API does not necessarily represent a single tracker.
  • Data can change frequently, as trackers sync at different intervals and the unified data is recalculated at each sync.
  • Distance and number of steps are correlated when GPS data is available.

Interpreting the Sleep Stage and Short Data

When sleep data is displayed through the Web APIs, the data is represented in 2 ways:

  1. The "data" grouping displays the sleep stages and any wake periods > 3 minutes (180 seconds).
  2. The "shortData" grouping displays the short wake periods representing physiological awakenings that are <= 3 minutes (180 seconds).

The “shortData" is separated to provide better visualization when evaluating the sleep data. Even though the short wakes are not included as a wake stage within the “data” grouping, the short wakes should be considered awake. There will be some overlap between the “shortData” and the sleep stage “data”. The “shortData” will take precedence by overriding any of the sleep stages in “data”. Also, the “shortData” is allowed to span over multiple stages. As a result, the “shortData” should be added to the total wake time, and the overlapping time removed from the stage's data resulting in a bisection of the sleep stage.

For example, let's say we have the following sleep stage and short data information:

     "levels": {
        "data": [
          {
            "dateTime": "2020-01-30T01:43:30.000",
            "level": "rem",
            "seconds": 180
          },
          {
            "dateTime": "2020-01-30T01:46:30.000",
            "level": "light",
            "seconds": 60
          },
        ],
        "shortData": [
          {
            "dateTime": "2020-01-30T01:44:30.000",
            "level": "wake",
            "seconds": 60
          }
        ]

The “rem” stage lasts for 3 minutes and ends at 01:46:30.000. During the “rem” stage, there is a short wake period at 01:44:30.000 lasting for 1 minute. This short wake splits the “rem” stage into 2 separate stages of rem sleep. When calculating the summary data, these periods of shortData need to be considered. In this case, the short “wake” length and count need to be added to the sleep “wake” summary. The “rem” summary count should be increased by 1 and the “rem” seconds are subtracted by the length of the short “wake” period. When the short wake period appears at the beginning or end of a sleep stage, the sleep stage count will not increase.

In actuality, the data ends up looking like:

          {
            "dateTime": "2020-01-30T01:43:30.000",
            "level": "rem",
            "seconds": 90
          },
          {
            "dateTime": "2020-01-30T01:45:00.000",
            "level": "wake",
            "seconds": 60
          }
          {
            "dateTime": "2020-01-30T01:46:00.000",
            "level": "rem",
            "seconds": 30
          },
          {
            "dateTime": "2020-01-30T01:46:30.000",
            "level": "light",
            "seconds": 60
          }

When verifying the sleep summary totals, it might be easier to visualize by expanding the “data” into 30 second periods, then set all of the time periods specified in “shortData” to wake. This table represents the previous example in 30 second periods.

dateTime level seconds
2020-01-30T01:43:30.000 rem 30
2020-01-30T01:44:00.000 rem 30
2020-01-30T01:44:30.000 rem 30
2020-01-30T01:45:00.000 wake 30
2020-01-30T01:45:30.000 wake 30
2020-01-30T01:46:00.000 rem 30
2020-01-30T01:46:30.000 light 30
2020-01-30T01:47:00.000 light 30

As a result, the final totals for this example end up being:

rem = 120 seconds with count = 2
light = 60 seconds with count = 1
wake = 60 seconds with count = 1

To calculate the minutesAsleep and minutesInBed, consider

  • minutesAsleep = deep + light + rem
  • minutesInBed = deep + light + rem + wake

Subscriber Security

Subscription notifications notify the application that new user data is available to retrieve. Without added security, attackers may post false notifications to your endpoints causing unnecessary load and data retrievals. Therefore, it's important to confirm the notifications are coming from Fitbit.

The subscription API is scaled dynamically as necessary to deliver notifications in real-time. Do not attempt to create a "whitelist" of Fitbit IP addresses for your subscriber endpoint. Instead, use the X-Fitbit-Signature header and FCrDNS to verify the IP Aaddress is coming from a Fitbit domain.

X-Fitbit-Signature

To confirm that a notification originated from Fitbit you may verify the X-Fitbit-Signature HTTP header value. Compute the expected signature using the following method:

  • Look up the client secret listed for your application on dev.fitbit.com
  • Append the & character to the client secret to form the signing key, e.g. 123ab4567c890d123e4567f8abcdef9a&
  • Using a cryptographic library, hash the JSON body of the notification with the HMAC-SHA1 algorithm and the above signing key. The body begins with a [ character and ends with a ] character, inclusive.
  • BASE64 encode the result of the hash function.
  • Finally, verify the BASE64 encoded value matches the value of the X-Fitbit-Signature header.
NOTE: This method is similar to the Authorization Header oauth_signature parameter described in RFC5849 but does not utilize parameter encoding.

If signature verification fails, respond with a 404 to avoid revealing your application to a potential attacker. We recommend logging the remote IP of the host sending the incorrect signature, the incoming signature, and incoming message content. We ask that you send us a copy of this information so we can investigate.

Signature verification is optional, but recommended.

Example

Suppose the application's client secret is 123ab4567c890d123e4567f8abcdef9a, and the incoming notification body is

[
  {
    "collectionType": "foods",
    "date": "2020-06-01",
    "ownerId": "X1Y2Z3",
    "ownerType": "user",
    "subscriptionId": "1234"
  }
]

The pseudocode for generating the signature is

BASE64(HMAC-SHA1(body, "123ab4567c890d123e4567f8abcdef9a&"))

resulting with the X-Fitbit-Signature value of

Oyv+HBziS4dH/fHJ735cToXX6vs=

Forward-Confirmed Reverse DNS

FCrDNS allows the firewall or application to verify that an IP address making a request to your subscriber endpoint is a legitimate fitbit.com server. For example, your subscriber endpoint receives a request from the IP address 75.126.122.162. First, do a reverse DNS lookup to retrieve the hostname of the server. Verify that this returns a subdomain on fitbit.com. Then, do a DNS lookup on that hostname to verify it resolves to the IP address of the request.

In Linux, Unix, and macOS, this looks like:
$ host 75.126.122.162
162.122.126.75.in-addr.arpa domain name pointer api-75-126-122-162.fitbit.com.
$ host api-75-126-122-162.fitbit.com
api-75-126-122-162.fitbit.com has address 75.126.122.162
Note: For users using Windows, use the nslookup command.