Skip to main content

Garmin - FIT Python SDK

FIT SDK Documentation

The FIT SDK documentation is available at https://developer.garmin.com/fit.

FIT SDK Developer Forum

Share your knowledge, ask questions, and get the latest FIT SDK news in the FIT SDK Developer Forum.

FIT Python SDK Requirements

  • Python Version 3.6 or greater is required to run the FIT Python SDK

Install

The FIT Python SDK is published to PyPi as garmin-fit-sdk and can be installed using pip.

pip install garmin-fit-sdk

Usage

from garmin_fit_sdk import Decoder, Stream

stream = Stream.from_file("Activity.fit")
decoder = Decoder(stream)
messages, errors = decoder.read()

print(errors)
print(messages)

Decoder

Constructor

Creating Decoder objects requires an input Stream representing the binary FIT file data to be decoded. See Creating Streams for more information on constructing Stream objects.

Once a Decoder object is created it can be used to check that the Stream is a FIT file, that the FIT file is valid, and to read the contents of the FIT file.

is_fit Method

All valid FIT files should include a 12 or 14 byte file header. The 14 byte header is the preferred header size and the most common size used. Bytes 8-11 of the header contain the ASCII values ".FIT". This string can easily be spotted when opening a binary FIT file in a text or hex editor.

  Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 0E 10 43 08 78 06 09 00 2E 46 49 54 96 85 40 00    ..C.x....FIT..@.
00000010: 00 00 00 07 03 04 8C 04 04 86 07 04 86 01 02 84    ................
00000020: 02 02 84 05 02 84 00 01 00 00 19 28 7E C5 95 B0    ...........(~E.0

check_integrity Method

The checkIntegrity method performs three checks on a FIT file:

  1. Checks that bytes 8-11 of the header contain the ASCII values ".FIT".
  2. Checks that the total file size is equal to Header Size + Data Size + CRC Size.
  3. Reads the contents of the file, computes the CRC, and then checks that the computed CRC matches the file CRC.

A file must pass all three of these tests to be considered a valid FIT file. See the IsFIT(), CheckIntegrity(), and Read() Methods recipe for use-cases where the checkIntegrity method should be used and cases when it might be better to avoid it.

Read Method

The Read method decodes all messages from the input stream and returns an object containing a list of errors encountered during the decoding and a dictionary of decoded messages grouped by message type. Any exceptions encountered during decoding will be caught by the Read method and added to the list of errors.

The Read method accepts an optional options object that can be used to customize how field data is represented in the decoded messages. All options are enabled by default. Disabling options may speed up file decoding. Options may also be enabled or disabled based on how the decoded data will be used.

messages, errors = read(
            apply_scale_and_offset = True,
            convert_datetimes_to_dates = True,
            convert_types_to_strings = True,
            enable_crc_check = True,
            expand_sub_fields = True,
            expand_components = True,
            merge_heart_rates = True,
            mesg_listener = None)

mesg_listener

Optional callback function that can be used to inspect or manipulate messages after they are fully decoded and all the options have been applied. The message is mutable and will be returned from the Read method in the messages dictionary.

Example mesg_listener callback that tracks the field names across all Record messages.

from garmin_fit_sdk import Decoder, Stream, Profile

stream = Stream.from_file("Activity.fit")
decoder = Decoder(stream)

record_fields = set()
def mesg_listener(mesg_num, message):
    if mesg_num == Profile['mesg_num']['RECORD']:
        for field in message:
            record_fields.add(field)

messages, errors = decoder.read(mesg_listener = mesg_listener)

if len(errors) > 0:
    print(f"Something went wrong decoding the file: {errors}")
    return

print(record_fields)

apply_scale_and_offset: true | false

When true the scale and offset values as defined in the FIT Profile are applied to the raw field values.

{
  'altitude': 1587 ## with a scale of 5 and offset of 500 applied
}

When false the raw field value is used.

{
  'altitude': 10435 ## raw value stored in file
}

enable_crc_check: true | false

When true the CRC of the file is calculated when decoding a FIT file and then validated with the CRC found in the file. Disabling the CRC calculation will improve the performance of the read method.

expand_sub_fields: true | false

When true subfields are created for fields as defined in the FIT Profile.

{
  'event': 'rear_gear_change',
  'data': 16717829,
  'gear_change_data':16717829 ## Sub Field of data when event == 'rear_gear_change'
}

When false subfields are omitted.

{
  'event': 'rearGearChange',
  'data': 16717829
}

expand_components: true | false

When true field components as defined in the FIT Profile are expanded into new fields. expand_sub_fields must be set to true in order for subfields to be expanded

{
  'event': 'rear_gear_change'
  'data': 16717829,
  'gear_change_data':16717829, ## Sub Field of data when event == 'rear_gear_change'
  'front_gear': 2, ## Expanded field of gear_change_data, bits 0-7
  'front_gear_num': 53, ## Expanded field of gear_change_data, bits 8-15
  'rear_gear': 11, ## Expanded field of gear_change_data, bits 16-23
  'rear_gear_num': 1, ## Expanded field of gear_change_data, bits 24-31
}

When false field components are not expanded.

{
  'event': 'rear_gear_change',
  'data': 16717829,
  'gear_change_data': 16717829 ### Sub Field of data when event == 'rear_gear_change'
}

convert_types_to_strings: true | false

When true field values are converted from raw integer values to the corresponding string values as defined in the FIT Profile.

{ 'type':'activity'}

When false the raw integer value is used.

{ 'type': 4 }

convert_datetimes_to_dates: true | false

When true FIT Epoch values are converted to Python datetime objects.

{ 'time_created': {Python datetime object} }

When false the FIT Epoch value is used.

{ 'time_created': 995749880 }

When false the Util.convert_timestamp_to_datetime method may be used to convert FIT Epoch values to Python datetime objects.

merge_heart_rates: true | false

When true automatically merge heart rate values from HR messages into the Record messages. This option requires the apply_scale_and_offset and expand_components options to be enabled. This option has no effect on the Record messages when no HR messages are present in the decoded messages.

Creating Streams

Stream objects contain the binary FIT data to be decoded. Streams objects can be created from bytearrays, BufferedReaders, and BytesIO objects. Internally the Stream class uses a BufferedReader to manage the byte stream.

From a file

stream = Stream.from_file("activity.fit")
print(f"is_fit: {Decoder.is_fit(stream)}")

From a bytearray

fit_byte_array = bytearray([0x0E, 0x10, 0xD9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, 0x91, 0x33, 0x00, 0x00])
stream = Stream.from_byte_array(fit_byte_array)
print(f"is_fit: {Decoder.is_fit(stream)}")

From a BytesIO Object

fit_byte_bytes_io = io.BytesIO(bytearray([0x0E, 0x10, 0xD9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, 0x91, 0x33, 0x00, 0x00]))
stream = Stream.from_byte_io(fit_byte_bytes_io)
print(f"is_fit: {Decoder.is_fit(stream)}")

From a buffered_reader

fit_buffered_reader = io.BufferedReader(io.BytesIO(bytearray([0x0E, 0x10, 0xD9, 0x07, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x46, 0x49, 0x54, 0x91, 0x33, 0x00, 0x00])))
stream = Stream.from_buffered_reader(fit_buffered_reader)
print(f"is_fit: {Decoder.is_fit(stream)}")

Util

The Util object contains both constants and methods for working with decoded messages and fields.

FIT_EPOCH_S Constant

The FIT_EPOCH_S constant represents the number of seconds between the Unix Epoch and the FIT Epoch.

FIT_EPOCH_S = 631065600

The FIT_EPOCH_S value can be used to convert FIT Epoch values to Python datetime objects.

python_date = datetime.datetime.fromtimestamp(fitDateTime + FIT_EPOCH_S, datetime.UTC)

BASE_TYPE_TO_FIELD_TYPE Constant

BASE_TYPE_TO_FIELD_TYPE is a dictionary that maps FIT base type values to their corresponding field type name strings as defined in the FIT Profile.

field_type_string = BASE_TYPE_TO_FIELD_TYPE[base_type_value]
# e.g. BASE_TYPE_TO_FIELD_TYPE[BASE_TYPE['UINT32']] == 'uint32'

FIELD_TYPE_TO_BASE_TYPE Constant

FIELD_TYPE_TO_BASE_TYPE is the inverse mapping of BASE_TYPE_TO_FIELD_TYPE. It maps FIT field type name strings to their corresponding base type values as defined in the FIT Profile.

base_type_value = FIELD_TYPE_TO_BASE_TYPE[field_type_string]
# e.g. FIELD_TYPE_TO_BASE_TYPE['uint32'] == BASE_TYPE['UINT32']

convert_timestamp_to_datetime Method

A convenience method for converting FIT Epoch values to Python Datetime objects.

python_date = convert_timestamp_to_datetime(fit_datetime)

convert_datetime_to_timestamp Method

A convenience method for converting Python Datetime objects to FIT Epoch values.

fit_datetime = convert_datetime_to_timestamp(python_date)

Encoder

Usage

from datetime import datetime, timezone

from garmin_fit_sdk import Encoder, Profile

encoder = Encoder()

# Pass the MesgNum and message data as separate parameters to the onMesg() method
encoder.on_mesg(Profile['mesg_num']['FILE_ID'], {
    'manufacturer': 'development',
    'product': 1,
    'time_created': datetime.now(tz=timezone.utc),
    'type': 'activity',
})

# The writeMesg() method expects the mesgNum to be included in the message data
# Internally, writeMesg() calls onMesg()
encoder.write_mesg({
    'mesg_num': Profile['mesg_num']['FILE_ID'],
    'manufacturer': 'development',
    'product': 1,
    'time_created': datetime.now(tz=timezone.utc),
    'type': 'activity',
})

# Unknown values in the message will be ignored by the Encoder
encoder.on_mesg(Profile['mesg_num']['FILE_ID'], {
    'manufacturer': 'development',
    'product': 1,
    'time_created': datetime.now(tz=timezone.utc),
    'type': 'activity',
    'customField': 12345, # This value will be ignored by the Encoder
})

# Subfield values in the message will be ignored by the Encoder
encoder.on_mesg(Profile['mesg_num']['FILE_ID'], {
    'manufacturer': 'development',
    'product': 4440, # This is the main product field, which is a uint16
    'garmin_product': 'edge_1050', # This value will be ignored by the Encoder, use the main field value instead
    'time_created': datetime.now(tz=timezone.utc),
    'type': 'activity',
})

uint8_array = encoder.close()

# Write the bytes to a file
with open('example.fit', 'wb') as f:
    f.write(uint8_array)

See the Encode Activity Recipe for a complete example of encoding a FIT Activity file using the FIT Python SDK.

Metadata

Release files for garmin-fit-sdk 21.217.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for garmin-fit-sdk 21.217.0
File Size Uploaded
garmin_fit_sdk-21.217.0.tar.gz 208.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for garmin-fit-sdk 21.217.0
File Interpreter ABI Platform
garmin_fit_sdk-21.217.0-py3-none-any.whl Python 3 none any Details

Total release size: 437.9 kB

Release files / garmin_fit_sdk-21.217.0.tar.gz

Download URL garmin_fit_sdk-21.217.0.tar.gz
Size 208.2 kB
Tags Source
SHA-256 checksum
How to use checksums
df88c37cb0b28cdebbb1b0aeb9c514b078c832a0ab4e4f4ca14d19ccecd27ed2
BLAKE2b-256 checksum
How to use checksums
5b071b21a25bb3fdf4f3d298bed89fab08bb57556c4eb81444589f568fd410be
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log

Release files / garmin_fit_sdk-21.217.0-py3-none-any.whl

Download URL garmin_fit_sdk-21.217.0-py3-none-any.whl
Size 229.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
382bc4cba7cc3e26bd6d65fbf5ad6864cf15feeafcf21dd226e2697df1063e96
BLAKE2b-256 checksum
How to use checksums
b1d1a029c6d76246361be4d7126c2f6a209b63bc6f44e4b855978d178f5632eb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 22, 2026.

Transparency log
Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page