Skip to main content

A modern Python client for the Concept2 Logbook API.

Project description

pyconcept2

pyconcept2 is a modern Python client for the Concept2 Logbook API.

The project is intentionally small for now: it provides a typed client, clean exceptions, and pydantic models that can grow with the API surface.

Features

  • Read the authenticated athlete profile.
  • Read workouts/results, including paginated access and all-page convenience.
  • Fetch workout details, stroke data, and file exports.
  • Derive custom time splits from stroke data.
  • Read Concept2 Logbook challenges without an access token.
  • Use typed pydantic models while tolerating new API fields.

Installation

pip install pyconcept2

For local development:

pip install -e ".[dev]"

Authentication

Authenticated Logbook endpoints require a Concept2 access token:

from pyconcept2 import Concept2Client

client = Concept2Client(access_token="...")

Or load the token from CONCEPT2_ACCESS_TOKEN:

from pyconcept2 import Concept2Client

client = Concept2Client.from_env()

For read-only workout access, the token needs the Concept2 results:read scope. Profile access needs user:read. Logbook challenge endpoints are public and can be used without a token:

client = Concept2Client()

Usage

from pyconcept2 import Concept2Client

client = Concept2Client(access_token="...")
profile = client.get_profile()

print(profile.username)

Fetch workouts:

from pyconcept2 import Concept2Client

client = Concept2Client(access_token="...")
workouts = client.get_workouts(newest=10)

for workout in workouts:
    print(
        workout.date,
        workout.machine_type,
        workout.distance,
        workout.time_formatted,
        workout.calories,
    )

Filter workouts:

workouts = client.get_workouts(
    from_date="2026-01-01",
    to_date="2026-01-31",
    workout_type="rower",
    number=100,
)

Fetch every available page:

workouts = client.get_workouts(all_pages=True, number=250)

Fetch a page with pagination metadata:

page = client.get_workouts_page(page=1, number=25)

print(page.pagination.total if page.pagination else None)
for workout in page.items:
    print(workout.id, workout.distance)

Fetch workout details:

workout = client.get_workout(12345, include=["strokes", "metadata", "user"])

print(workout.id)
print(workout.calories)
print(workout.strokes[0].spm if workout.strokes else None)

if workout.details:
    for split in workout.details.splits:
        print(split.time, split.distance, split.calories, split.stroke_rate)

    for interval in workout.details.intervals:
        print(interval.type, interval.time, interval.distance)

Fetch stroke data:

strokes = client.get_strokes(12345)

for stroke in strokes:
    print(stroke.t, stroke.d, stroke.spm, stroke.hr)

Derive 2-minute splits from stroke data:

splits = client.get_time_splits(12345, split_time=1200)

for split in splits:
    print(
        split.time_total_formatted,
        split.distance_total,
        split.time_formatted,
        split.distance,
        split.pace_formatted,
        split.stroke_rate,
    )

split.pace is the raw value in tenths of a second per 500m for RowErg and SkiErg workouts. Use split.pace_formatted for display, e.g. 2:38.7.

Export a workout file:

fit_file = client.export_workout(12345, "fit")
csv_file = client.export_workout(12345, "csv")
tcx_file = client.export_workout(12345, "tcx")

Summarize workouts:

summary = client.get_workout_summary(workout_type="rower")

print(summary.count)
print(summary.distance)
print(summary.time)
print(summary.calories)

The client can also be used as a context manager:

from pyconcept2 import Concept2Client

with Concept2Client(access_token="...") as client:
    profile = client.get_profile()

Logbook challenge endpoints do not require an access token:

from pyconcept2 import Concept2Client

client = Concept2Client()

challenges = client.get_challenges(number=25)
current = client.get_current_challenges()
upcoming = client.get_upcoming_challenges(days=60)
recent = client.get_recent_challenges(days=60)
season = client.get_challenges_for_season(2026)
events = client.get_events_for_year(2026)

API Overview

Authenticated methods:

  • get_profile()
  • get_workouts(...)
  • get_workouts_page(...)
  • get_workout(result_id, include=None)
  • get_strokes(result_id)
  • get_time_splits(result_id, split_time=1200)
  • export_workout(result_id, export_type="csv")
  • get_workout_summary(...)

Public challenge methods:

  • get_challenges(...)
  • get_current_challenges()
  • get_upcoming_challenges(days=None)
  • get_recent_challenges(days=None)
  • get_challenges_for_season(season)
  • get_events_for_year(year)

Units

Concept2 uses a few compact numeric units:

  • Workout and split time values are tenths of a second.
  • Stroke t values are tenths of a second.
  • Stroke d values are decimeters.
  • pace is tenths of a second per 500m for RowErg and SkiErg workouts.

For display, use fields such as pace_formatted where available:

splits = client.get_time_splits(12345)

for split in splits:
    print(split.pace, split.pace_formatted)

Development

PYTHONPATH=src pytest
PYTHONPATH=src ruff check src tests
PYTHONPATH=src mypy src

Status

This package is pre-alpha and intentionally read-only. OAuth helpers and workout write support are out of scope for now.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyconcept2-0.1.0.tar.gz (12.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyconcept2-0.1.0-py3-none-any.whl (10.0 kB view details)

Uploaded Python 3

File details

Details for the file pyconcept2-0.1.0.tar.gz.

File metadata

  • Download URL: pyconcept2-0.1.0.tar.gz
  • Upload date:
  • Size: 12.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyconcept2-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2159e87d3254582dad13df14908f0c3f14dff2570858ac0b0049e3a575df8ae9
MD5 2d9cab808bd21b4bc32fd72f15f8f6fb
BLAKE2b-256 4c7fd1fe60f693df8e4280c8b5b319a6c2012168ae7f573ce81453b20b775ea6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyconcept2-0.1.0.tar.gz:

Publisher: publish.yml on gickowtf/pyconcept2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyconcept2-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: pyconcept2-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 10.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for pyconcept2-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2498c45055b53a4c30f02ba43606c622f9803a17bc835f7fc8cd787295bd7ce1
MD5 d90a21dc04c7cca4aa31f45f6ae91a50
BLAKE2b-256 7ca50162a30a3f6d954e348000f45dd45b5d4cedabbca140cc6c3e9001804e6d

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyconcept2-0.1.0-py3-none-any.whl:

Publisher: publish.yml on gickowtf/pyconcept2

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page