Skip to content
This repository has been archived by the owner. It is now read-only.
Branch: master
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
517 lines (408 sloc) 17.4 KB

Fitbit Protocol Documentation


The Fitbit is a small clipon accelerometer that functions as a pedometer, distance meter, and calorie counter. It uses the ANT protocol to talk to a base, connected to a computer via USB. This document outlines communication both with the base unit/device via USB, as well as synchronizing with fitbit’s web service.

Important Document Information


We recommend the following references to go along with this document:


  • Base: Refers to the ANT base station, connected by USB to the host computer.

  • Tracker: The clip-on Fitbit device

Packet Documentation

Packet layouts in this document will use the full ANT packet to keep things consistent. The packet layout is documented in the protocol/message document, but is also listed here for sake of clarity:

  • Byte 0x0: Always 0xA4

  • Byte 0x1: Packet Data Length (up to 0x9)

  • Byte 0x2: Packet Type

  • Byte 0x2 + Packet Data Length: 8-bit checksum consisting of XOR’d bytes of packet. In packet layouts where we have variable bytes, the checksum is represented as ??.

What We Don’t Know

As this section will probably be updated frequently, I’m putting it near the top for now. This lists things I haven’t figured out about the fitbit’s protocol and client yet.

  • How the erase opcodes work

  • Exactly what data we’re getting from the website to send to the device in the erase step

  • How the seconds data is stored/parsed

  • What the encrypted attribute on opcodes in the website response xml means

  • How authentication setup with the website works

  • How device bonding with the website account works

Reverse Engineering Methodology

Reverse engineering the Fitbit is relatively easy. From the USB side, it is not using encryption to talk to the base station, and it communicates using an unmodified ANT protocol, so the basic gist of messages can be learned simply by reading the ANT protocol documentation. Any USB based filter driver may be used to log the data flowing to and from the base station.

For further analysis, the Fitbit’s service logs are amazingly helpful. For the windows installation of the Fitbit software, the logs are located at

C:\Documents and Settings\All Users\Application Data\Fitbit\Tracker\logs

Daily logs are stored in plain text with the filename format


There may be multiple log files generated. Each log file has a complete record of both USB and website communications, with copious amounts of annotation. Each call to and response from the device and website is logged and even sometimes documented.

The USB information in the logs truncates most of the ANT protocol specific information. USB communication looks something like

02/02 22:25:36 Setting channel id [number: 65535, type: 1, transmission: 1]...
02/02 22:25:36 [40] 0x00, 0x51, 0x00
02/02 22:25:36 Channel id set.
02/02 22:25:36 Opening channel...
02/02 22:25:36 [40] 0x00, 0x4b, 0x00
02/02 22:25:36 Channel opened.
02/02 22:25:42 [9A] 0x00, 0x58, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Acquired beacon.  Sending RESET packet...
02/02 22:25:42 Queuing packet to send...
02/02 22:25:42 >>>>> SEND 0x78, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Sending 8 bytes of ACK data...
02/02 22:25:42 [9A] 0x00, 0x58, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 [05] 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Pending acks: 0
02/02 22:25:42 Tracker reset.  Sending LINK REQUEST...
02/02 22:25:42 Queuing packet to send...
02/02 22:25:42 >>>>> SEND 0x78, 0x02, 0x8b, 0x7a, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Sending 8 bytes of ACK data...
02/02 22:25:42 [9A] 0x00, 0x58, 0xf7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 [05] 0x00, 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Pending acks: 0
02/02 22:25:42 Closing channel...
02/02 22:25:42 [40] 0x00, 0x4c, 0x00
02/02 22:25:42 Close request acknowledged...
02/02 22:25:42 [02] 0x00, 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
02/02 22:25:42 Waiting for close message -- ignoring incoming data
02/02 22:25:42 [07] 0x00, 0x01, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00

This is not a complete log of USB messages, however, the explanations of some of the messages are quite useful for translating the actions into code.

While working on the web communication, it was also found that error messages about communications flow were quite helpful. Errors are served as a comment block at the beginning of a response. For example:

<!-- Error: getDeviceInfoHandler should be getting result of one opCodes -->

<!-- Error: Tracker object is null. This is impossible within normal
flow. Something is hitting a
predefined flow. -->

These logs and messages were massively helpful in finishing the web protocol commmunication. Many thanks to the fitbit engineers for writing readable error messages and logs.

Issues With User Security

Not everything is happiness and sunshine, however.

While information stored on Fitbit’s website may not be of the utmost privacy importance (though having random people know when you sleep is a little creepy), this information should still be guarded by at least semisecure means. However, there are multiple situations where this is not the case.

During initial login via the client software, user passwords are passed to the website as part of POST data, in the clear. They are also stored to the text log files in the clear. This can be seen in the following log block, with my personal account data removed.

01/28 23:31:55 Sending 4357 bytes of HTML to UI...
01/28 23:31:55 Processing request...
01/28 23:31:55 Waiting for minimum display time to elapse [1000ms]...
01/28 23:31:56 Waiting for form input...
01/28 23:32:14 UI [\\.\pipe\Fitbit|kyle]: F
01/28 23:32:14 Processing action 'form'...
01/28 23:32:14 Received form input: email=[YOUR UNENCRYPTED EMAIL HERE]&password=[YOUR UNENCRYPTED PASSWORD HERE]&[other stuff]
01/28 23:32:14 Connecting [2]: POST to with data:
               email=[YOUR UNENCRYPTED EMAIL HERE]&password=[YOUR UNENCRYPTED PASSWORD HERE]&[other stuff]
01/28 23:32:14 Processing action 'http'...
01/28 23:32:14 Received HTTP response:

The URLs and POST data are saved, and the processing action is "http", not "https". Oddly enough, login on Fitbit’s actual website is https.

When syncing data to the website, no authentication is used, and all requests are sent in clear http. The tracker ID is bonded to the account of the user, and when the website receives the tracker ID, it responds with the user ID. Once again, since all of this happens in the clear, it would be easy to inject data into anyone’s account, via either ANT or website sniffing. Why one would do this is beyond me, just saying, is all.

Website Communication

Account Establishment and Tracker Bonding

Nothing yet known about this.

Data Synchronization

Communication with the website happens via HTTP requests to a REST API, with replies of XML blocks. These blocks contain opcodes to send to the device, as well as information the website will need to identify the device in later commands.

Opcodes and their results are encoded via base64.

The data flow between the website and the client happens in the follow order (all commands go to, REST locations are listed below):

  • Client receives beacon from tracker, establishes link (see Fitbit Communication section)

  • Client contacts website at /device/tracker/uploadData, sends basic client and platform information

  • Website replies with opcode for tracker data request

  • Client gets tracker data (serial number, firmware version, etc…​), sends base response. Sends to /device/tracker/dumpData/lookupTracker

  • Website replies with website tracker and user ids based on tracker serial number, and opcodes for data dumping

  • Client dumps data from device, sends to /device/tracker/dumpData/dumpData

  • Website replies with commands to erase data from device, and synchronize time (and possibly data?) with website.

  • Client sends successful responses on all opcodes (but no specific data) to website at /device/tracker/dumpData/clearDataConfigTracker

  • Website replies with command to close tracker, and have the beacon sleep for 15 minutes (probably conserves power).

Web Response XML Schema

Example XML Response
<?xml version="1.0" ?>
<fitbitClient version="1.0">
  <response host=""  path="/device/tracker/dumpData/dumpData" port="80">trackerPublicId=[REDACTED]&amp;userPublicId=[REDACTED]&amp;deviceInfo.serialNumber=[REDACTED]&amp;deviceInfo.hardwareRev=10&amp;deviceInfo.bslVerMajor=2&amp;deviceInfo.bslVerMinor=23&amp;deviceInfo.appVerMajor=2&amp;deviceInfo.appVerMinor=23&amp;deviceInfo.inModeBSL=false&amp;deviceInfo.onCharger=true&amp;parseData=on</response>
  <device type="tracker" pingInterval="4000" action="command" >
    <remoteOps errorHandler="executeTillError" responder="respondNoError">
      <remoteOp encrypted="false">
      <remoteOp encrypted="false">
      <remoteOp encrypted="false">
      <remoteOp encrypted="false">
      <remoteOp encrypted="false">

Fitbit Communication

This section covers communicate with the base and tracker via USB and ANT, in the context of the synchronization communications with the website.

It is assumed that the reader is familiar with the USB and ANT protocols. For those not familiar with ANT, it’s fairly easy to follow along with the Message and Protocol documentation in the references section. For those not familiar with USB, go outside and play. Preferably with your fitbit on.

Base Initialization

Initializing the base unit happens whenever the unit is plugged in, or the client program is brought up. This consists of sending USB control messages and ANT protocol messages to configure the device to the right baud rate, channel, etc…​

To start setting up the base station, we send a bank of control messages. The following is the control messages from the python library, with

  • Argument 1: bmRequestType

  • Argument 2: bmRequest

  • Argument 3: wValue

  • Argument 4: wIndex

  • Argument 5: data (If a list, send data. If an int, receive data.)

ctrl_transfer(0x40, 0x00, 0xFFFF, 0x0, [])
ctrl_transfer(0x40, 0x01, 0x2000, 0x0, [])
ctrl_transfer(0x40, 0x00, 0x0, 0x0, [])
ctrl_transfer(0x40, 0x00, 0xFFFF, 0x0, [])
ctrl_transfer(0x40, 0x01, 0x2000, 0x0, [])
ctrl_transfer(0x40, 0x01, 0x4A, 0x0, [])
# Receive 1 byte, should be 0x2
ctrl_transfer(0xC0, 0xFF, 0x370B, 0x0, 1)
ctrl_transfer(0x40, 0x03, 0x800, 0x0, [])
ctrl_transfer(0x40, 0x13, 0x0, 0x0,
              [0x08, 0x00, 0x00, 0x00,
               0x40, 0x00, 0x00, 0x00,
               0x00, 0x00, 0x00, 0x00,
               0x00, 0x00, 0x00, 0x00])
ctrl_transfer(0x40, 0x12, 0x0C, 0x0, [])

We then receive a packet of information, usually a reset validation packet.

After this, we initialize the base channel (via ANT messages) to:

  • Period: 0x1000

  • Frequency: 0x2

  • Transmit Power: 0x3

  • Search Timeout: 0xFF

  • Channel ID - Device Number: 0xFFFF, Device Type ID: 0x01, Trans Type: 0x01

This sets up the base to listen for the beacon packet from the tracker device.

Establishing Tracker Communication

Once the base is configured, it starts sending out read requests on channel 0. Assuming it hasn’t been told to sleep, the tracker sends out a beacon once a second on channel 0. The beacon is a data packet that looks like:

--> A4 09 4E 00 00 00 00 00 00 00 00 00 E3

Once the beacon is received by the base station, the base station sends a reset packet:

--> A4 09 4F 00 78 00 00 00 00 00 00 00 9A

Then, assuming the tracker is not on the base, it sends a "Channel Hop" message:

--> A4 09 4F 00 78 02 XX XX 00 00 00 00 ??

Where XXXX is a random 16-bit little-endian number denote the new channel for the device to talk on. The device and base switch to that channel, and the base runs the channel initialization sequences listed in the last step again, except this time with the channel ID set to the XXXX value instead of 0xFFFF. The base waits for the device beacon on this channel, and once received, initializes transfer.

Talking to the Tracker

Non-burst-data packets to the tracker take the form:

Sent Packet
--> A4 09 4F 00 3W XX 00 00 00 00 00 00 ??
  • W - Starts at 0x9, increments on every new packet send, wraps at 0xF to 0x8. From here on out, refered to as the sequence nibble.

  • XX - Command to send

  • All bytes after YY up to checksum - Data

We will usually get two packets back from the tracker

Received Packet
<-- A4 03 40 00 01 05 E3
<-- A4 09 4F 00 3Y ZZ 00 00 00 00 00 00 ??
  • Y - Should match W from packet sent

  • ZZ - Return status

If burst data is sent after the status is received, burst data packets will follow the 0x2X/0x4X/0x6X format outlined in the ANT documentation. See the ANT documentation on burst transfers for more info.

Querying Tracker Information

Now that the base is connected to the tracker, we can start querying the tracker for information about itself.

--> A4 09 4F 00 3X 24 00 00 00 00 00 00 ?? - Query
<-- A4 03 40 00 01 05 E3                   - ANT Acknowledgment
<-- A4 09 4F 00 3X 42 00 00 00 00 00 00 ?? - Device Contact Success
--> A4 09 4F 00 3Y 70 00 02 00 00 00 00 ?? - Query for Info
<-- A4 03 40 00 01 05 E3                   - ANT Acknowledgment
<-- A4 09 50 00 3Y 81 0C 00 00 00 00 ?? ?? - Start burst, 0x0C length, bytes after length random?
<-- A4 09 50 20 GG GG GG GG GG HH II JJ ?? - Device Data
<-- A4 09 50 C0 KK LL MM NN OO 00 D9 BD ?? - More Device Data

Taking the last two Device Data Portions:

  • Bytes 0-4 (G) - 5 byte Device Serial Number

  • Byte 5 (H) - Firmware Version

  • Byte 6 (I) - BSL Major Version

  • Byte 7 (J) - BSL Minor Version (i.e. II.JJ = bsl version)

  • Byte 8 (K) - App Major Version

  • Byte 9 (L) - App Minor Version

  • Byte 10 (M) - In BSL Mode

  • Byte 11 (N) - Is Device currently on the charger?

  • Byte 12 (O) - Unknown?

All remaining bytes up until checksum are ignored, can be anything.

Data Dumping

Once we’ve gotten the tracker information, it’s time to pull the data off the device. We send the following packet to the tracker:

--> A4 09 4F 00 3W 22 0X 00 00 00 00 00 ??
  • W - Sequence nibble

  • X - Data Type that burst requests will send back. Conjectures listed below based on value.

    • 0 - Daily information, or information generated on every read of the tracker?

    • 1 - 5 minute interval data

    • 2 - Second Information? Only seems to return last few seconds.

    • 3 - Haven’t tried it

    • 4 - Unknown

    • 5 - Unknown

After we send this, we get back the usual acknowledgement:

<--  A4 03 40 00 01 05 E3

And then begins the data dumping

-->  A4 09 4F 00 3W 60 00 02 XX 00 00 00 ??
  • W - Sequence nibble

  • XX - Memory Bank ID we’re querying

The memory bank id goes from 00 to whenever we’re out of data, which we can tell by the burst data returned from the command

<-- A4 03 40 00 01 05 E3
<-- A4 09 50 00 3W 81 YY YY 00 00 00 ?? ??
  • W - Matching sequence nibble for input command

  • YYYY - 16 bit little endian value for data length being transmitted, starting at the beginning of the next packet. We get 8 bytes of data per burst packet.

If YYYY is 0, we know that we’re querying a null bank, and have hit the end of the information type we’re currently getting. We can append all of the data received from all burst commands for this data type.

Device Erasing

I have no idea how this works. I’m just replaying what the website gives me right now. It sends over extra data, which could possibly be time synchronization, or information from the website (stride length, weight, etc?).

Data Formats

This section outlines the data sent over during the "Data Dump" portion of the communications.

Second Data - Data Type ID 0x2

Example Packet
e0 1d 52 4d 0f 02 05 85 00 00 e3 0b 00
Packet Layout
XX XX XX XX ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
  • XXXXXXXX - 32-bit little endian seconds since Jan 1 1970

Minute Interval Data - Data Type ID 0x0

Example Packet
4d 52 22 c0
81 1c 00
4d 52 27 ac
85 19 00
85 1c 13
Packet Layout
  • XXXXXXXX - 32-bit little endian seconds since Jan 1 1970

  • MM - Unknown, always seems to start around 0x81?

  • NN - Unknown

  • OO - Unknown

Daily/Per Sync Data - Data Type ID 0x1

Example Packet
72 24 52 4d 92 50 2a 2f 00 00 5b c9 8c 00
Packet Layout
XX XX XX XX ?? ?? YY YY ?? ?? ?? ?? ?? ??
  • XXXXXXXX - 32-bit little endian seconds since Jan 1 1970

  • YYYY - 16-bit little endian Steps since last day

You can’t perform that action at this time.