fitfast
Fast Garmin FIT file parsing for Python — a thin PyO3 binding around the excellent rustyfit Rust crate.
~36× faster than the official pure-Python Garmin SDK for the common "FIT file → NumPy/DataFrame" workflow (17× for dict decode, 60× for file validation), with output semantics that match the official SDK.
import fitfast
# Columnar decode: every numeric record field as a float64 NumPy array
cols = fitfast.records("activity.fit")
cols["heart_rate"] # array([ 96., 101., 103., ...])
cols["timestamp"] # Unix epoch seconds
cols["position_lat"] # degrees (semicircles converted for you)
# Works for any message kind
laps = fitfast.records("activity.fit", message="lap")
# Classic dict output (garmin-fit-sdk-style), enums as names
data = fitfast.parse("activity.fit")
data["session"][0]["sport"] # "running"
data["session"][0]["total_distance"] # 77265.02 (scale/offset applied)
# Cheap full-file validation and exploration
n_messages, n_fields = fitfast.count("activity.fit")
fitfast.message_counts("activity.fit") # {"file_id": 1, "record": 35079, ...}
All functions accept a path, bytes, bytearray, memoryview, or a binary
file-like object.
Install
pip install fitfast
Prebuilt wheels are available for Linux, macOS, and Windows (CPython ≥ 3.10, one abi3 wheel per platform). Building from source requires a Rust toolchain.
Why this package
Every existing Python FIT parser decodes the binary format in pure Python.
The FIT format is dense — a single long
activity is easily 100k+ messages and >1M fields — and pure-Python decoding
costs seconds per file, which hurts anyone batch-processing an activity
archive. Meanwhile the fastest FIT decoder we measured in any language is a
Rust crate (see roznet/fit-benchmarks).
fitfast bridges that gap: it is (to our knowledge) the first Python FIT
package backed by a native core.
Two design choices keep the speedup from evaporating at the FFI boundary:
- Columnar first.
records()returns NumPy arrays built in Rust — no per-message Python objects at all. This is the fast path for analytics (pandas/polars/matplotlib) and the reason the end-to-end speedup stays ~36×. - Dicts when you want them.
parse()produces the familiar dict-of-messages shape with profile names, scale/offset, enum names, and invalid-value handling matching the official SDK — still 17× faster than pure-Python parsers, because only the final objects are built in Python.
Benchmarks
Decoding a 3.6 MB, 147,482-message activity file (a 77 km ultra with 35k GPS records), Apple M-series, median of repeated in-process runs:
Task: FIT → NumPy columns for all record messages (31 columns) |
Time | Relative |
|---|---|---|
fitfast.records() |
0.075 s | 1× |
garmin-fit-sdk (official) + NumPy conversion |
2.68 s | 36× slower |
| Task: FIT → per-message dicts | Time | Relative |
|---|---|---|
fitfast.parse() |
0.15 s | 1× |
garmin-fit-sdk (official, pure Python) |
2.57 s | 17× slower |
fitdecode |
3.88 s | 26× slower |
fitparse |
6.72 s | 45× slower |
| Task: validate the whole file (structure + CRCs) | Time | Relative |
|---|---|---|
fitfast.count() |
0.043 s | 1× |
garmin-fit-sdk (official) |
2.57 s | 60× slower |
Reproduce with benchmarks/bench.py
and any FIT file:
python benchmarks/bench.py path/to/activity.fit
API
fitfast.records(source, message="record", *, degrees=True)
Columnar decode of one message kind → dict[str, np.ndarray] (float64).
- Every numeric scalar field that occurs in any message of that kind becomes a
column; rows where the field is absent or invalid are
NaN. - Profile scale/offset applied;
date_timefields become Unix seconds; semicircle coordinates become degrees (disable withdegrees=False). - Developer fields are included under their
field_descriptionnames. - String- and array-valued fields are skipped (use
parse()for those). - Unknown message kinds return
{}.
fitfast.parse(source, *, enum_names=True, datetimes=False)
Full decode → dict[str, list[dict]], keyed by message name
("record", "session", ...; manufacturer-specific messages as
"unknown_<num>").
- Semantics follow the official Garmin FIT SDK: scale/offset applied, fields holding the FIT "invalid" sentinel omitted, developer fields resolved.
enum_names=Truerenders profile enums as names ("running"), unknown values stay numeric.datetimes=Trueyields timezone-aware UTCdatetimeobjects instead of Unix epoch ints.- Fields not in the profile are keyed by integer field number (matching
garmin-fit-sdk); the same fields appear as
unknown_<num>columns inrecords(), where keys must be strings. - Array fields keep invalid elements as
None;BYTE-typed fields are returned as Pythonbytes(opaque payloads, e.g.application_id).
fitfast.count(source) / fitfast.message_counts(source)
Fast full-file validation → (n_messages, n_fields), and per-kind message
counts. Structure and CRCs are verified while decoding; invalid input raises
fitfast.FitDecodeError (a ValueError subclass).
Notes and current limitations
- Chained FIT files (multiple FIT sequences in one file) are decoded fully.
local_date_timefields are converted with the same FIT-epoch offset asdate_time(no timezone shift is applied — FIT does not store the offset).- Compressed-timestamp headers, component expansion (
enhanced_speed, ...) and accumulated fields are handled by the rustyfit decoder. - Dynamic sub-fields are not yet resolved (the main field's name is used) — planned for a future release.
records()intentionally returns only numeric scalars.- Files are decoded fully into memory; decoded messages can take an order of magnitude more RAM than the file size.
Credits
All of the actual FIT decoding is done by
rustyfit by
Hikmatulloh Hari Mukti (BSD-3-Clause), the
fastest FIT implementation we measured in any language — see also their Go
implementation muktihari/fit. If you need
FIT parsing in Rust or Go, use those crates directly. fitfast adds only the
Python-facing layer: columnar extraction, official-SDK-compatible value
semantics, and the PyO3 bindings.
Cross-language performance context: roznet/fit-benchmarks.
License
fitfast is licensed under the
BSD 3-Clause License.
Bundled third-party code: the compiled extension statically links rustyfit (BSD-3-Clause) and rust-numpy (BSD-2-Clause), see THIRD-PARTY-NOTICES.md.
The FIT Protocol itself is proprietary to Garmin; use of FIT-decoding software may require compliance with the FIT Protocol License. This project is not affiliated with or endorsed by Garmin.
Development
uv venv && uv pip install -e '.[dev]' # or: pip install -e '.[dev]'
maturin develop --release # rebuild the extension
pytest # run the test suite
The test suite builds synthetic FIT files byte-by-byte (little/big endian,
compressed timestamps, developer fields, chained files, corrupt CRCs) and
cross-checks output against the official garmin-fit-sdk.
Metadata
Release files for fitfast 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fitfast-0.1.0.tar.gz | 30.3 kB | Details |
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| fitfast-0.1.0-cp310-abi3-win_amd64.whl | CPython 3.10 | abi3 | Windows x86-64 | Details |
| fitfast-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ x86-64 | Details |
| fitfast-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl | CPython 3.10 | abi3 | Linux glibc 2.17+ ARM64 | Details |
| fitfast-0.1.0-cp310-abi3-macosx_11_0_arm64.whl | CPython 3.10 | abi3 | macOS 11.0+ ARM64 | Details |
| fitfast-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl | CPython 3.10 | abi3 | macOS 10.12+ x86-64 | Details |
Total release size: 1.8 MB
Release files / fitfast-0.1.0.tar.gz
| Download URL | fitfast-0.1.0.tar.gz |
|---|---|
| Size | 30.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
11fc0e470ee5a780ff676ad69838f68b2d27e1328a1eaba03c74a340bf1d40a7
|
|
BLAKE2b-256 checksum How to use checksums |
929a37e4e7410572ad909c172ca16ffe221974e844e6b2e2e33ff5ce8a579196
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency logRelease files / fitfast-0.1.0-cp310-abi3-win_amd64.whl
| Download URL | fitfast-0.1.0-cp310-abi3-win_amd64.whl |
|---|---|
| Size | 289.3 kB |
| Tags | CPython 3.10 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
21b217cccb4e0dea7f5efce1a99249981ae35e903f149ba2b1909bfdd31e130b
|
|
BLAKE2b-256 checksum How to use checksums |
fe1dd444b639c1c059f8b5ee3769036c1c9a7968b43e946016e6f15d2be0746b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency logRelease files / fitfast-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | fitfast-0.1.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 376.7 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
3297da4af1cd1716e5ad0f8b7c0dc832ae292aac65c81db0ca51e6e87f69de54
|
|
BLAKE2b-256 checksum How to use checksums |
e38bbd913c2b8d0016425d3be802ae252f426c694951757217097cf8c221353f
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency logRelease files / fitfast-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
| Download URL | fitfast-0.1.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl |
|---|---|
| Size | 356.3 kB |
| Tags | CPython 3.10 Linux glibc 2.17+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
fe7fd3f95712ea8ff4be04776e5e74d4b7c804cdc85619bca838d69b485156c9
|
|
BLAKE2b-256 checksum How to use checksums |
6b3b3032caabf9c0f23eccb8dee5f02f5fadc5268ec2076ffc55aa2dcf37db0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency logRelease files / fitfast-0.1.0-cp310-abi3-macosx_11_0_arm64.whl
| Download URL | fitfast-0.1.0-cp310-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 340.9 kB |
| Tags | CPython 3.10 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
bde5b4f78b74c89084a14dfede40fc0b244d891684f933d749347ff70c9c86d1
|
|
BLAKE2b-256 checksum How to use checksums |
13dce50a5a7e46814a820ae98e0deed45e686b63024b38f25d9d0144ab623823
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency logRelease files / fitfast-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl
| Download URL | fitfast-0.1.0-cp310-abi3-macosx_10_12_x86_64.whl |
|---|---|
| Size | 364.2 kB |
| Tags | CPython 3.10 abi3 macOS 10.12+ x86-64 |
|
SHA-256 checksum How to use checksums |
79d4fc6f9aab7c01abf374981407a90b28b9e6b2232899a12818304223c4404c
|
|
BLAKE2b-256 checksum How to use checksums |
9bf896e4f0f22af42427da1301e7db5aef402614e7b5e3293a65c63c3c92eed1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
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 Aug 29, 2026.
Transparency log