Skip to content

mlh-csis/tp-api-cli

Repository files navigation

trainingpeaks-unofficial

CI Python uv Pydantic Status

Unofficial CLI for inspecting TrainingPeaks data and creating/managing TrainingPeaks planned workouts via tpapi.trainingpeaks.com.

⚠️ Not affiliated with or supported by TrainingPeaks.

Features

  • Automated workout construction: handles metric estimation (TSS/NP), geometry rebuilding (for pace), and structure validation in one atomic step.
  • Self-healing auth: transparent bearer token management, refresh, and retry logic.
  • List, fetch, and week/range view ATP plans from the CLI
  • Athlete readiness/performance insight snapshots from the CLI
  • Typed internal models with Pydantic
  • Interactive CLI login (tp auth login) with macOS Keychain support (via keyring)

Installation

git clone <your-repo-url>
cd trainingpeaks-unofficial
uv sync

Quick start

uv sync

# first-time auth setup
uv run tp auth login

# auth/context check
uv run tp auth whoami

# list workouts for current week (default)
uv run tp workouts list

# machine mode
uv run tp --json workouts list

CLI global flags

  • --json / -j: machine envelope mode for all commands
  • --athlete-id / -a: explicit athlete ID override
  • --timeout: HTTP timeout seconds
  • --base-url: API base URL override
  • --verbose / -v: expanded human output where supported

JSON mode contract

Success:

{"ok": true, "data": {}, "warnings": ["..."]}

(warnings is omitted when empty.)

Failure:

{"ok": false, "error": {"type": "validation_error|auth_error|api_error|runtime_error", "message": "...", "details": {}}}

CLI command matrix (v1)

Command Purpose Notes
tp auth login [--insecure-store] [--username USER -u] [--password PASS -p] Interactive or non-interactive username/password login --json unsupported. Interactive mode requires TTY; non-interactive mode accepts --username / --password (both required together). On macOS stores cookie in Keychain by default. Non-macOS requires --insecure-store.
tp auth status [--check] Show local auth state --check performs a live API validation call
tp auth logout Clear persisted auth Removes keychain secret (macOS) and ~/.tp/auth.json
tp auth whoami Show resolved + inferred athlete identity Athlete resolution: flag -> store -> inferred
tp workouts list [--start YYYY-MM-DD --end YYYY-MM-DD] List workouts --start and --end must be provided together; defaults to current ISO week when omitted. tp workouts and tp workouts --list are accepted aliases for current-week listing.
tp workouts get <workout-id> Get workout summary Human output includes structure hint
tp workouts details <workout-id> Get workout analytics/details Human output is compact summary tables (zones/mean-max/files) and renders speed as pace (min/km). JSON output keeps raw speed fields and adds additive pace fields (e.g. *PaceMinPerKm).
tp workouts create [--payload-file FILE|-] [--structure-file FILE|-] [--day YYYY-MM-DD] [--type/--workout-type TYPE] [--title ...] [--description ...] [--auto-estimate/--no-auto-estimate] [--force-reestimate] Create workout If --payload-file is omitted, --day and --type are required. Flags override file fields. Missing title auto-generates <Type> Workout.
tp workouts update <workout-id> [--payload-file FILE|-] [--structure-file FILE|-] [--day YYYY-MM-DD] [--type/--workout-type TYPE] [--title ...] [--description ...] [--post-comment ...] [--auto-estimate/--no-auto-estimate] [--force-reestimate] Update planned workout Applies a patch to an existing workout. Provide at least one update field (payload/structure/flags). Flags override file fields when both are present.
tp workouts comment <workout-id> --text "..." Create a post-activity comment Uses the workout comments endpoint (fitness/v2) to persist a comment on the workout.
tp workouts estimate [--payload-file FILE|-] [--structure-file FILE|-] [--type/--workout-type TYPE] [--cache-policy read-through|refresh|cache-only] Estimate IF/TSS without creating a workout Requires workout type + structure. Default cache policy is read-through; use refresh to force API fetch or cache-only for strict offline mode.
tp workouts delete <workout-id> [--yes] Delete workout Prompts in human mode unless --yes; JSON mode requires --yes
tp workouts analysis <workout-id> [--cache-dir DIR] FIT file analysis for completed workouts Downloads and decodes FIT files for the given workout, showing detailed per-lap/session metrics. Default cache dir is ~/.tp/fit-cache.
tp workouts report <workout-id> [--format html|json] [--output PATH] [--detail summary|full] [--no-baseline] [--baseline-weeks N] [--baseline-size N] [--duration-tolerance-pct N] [--strict-name-match] [--write-json-sidecar] Generate a deep-dive workout report Requires complete workout data (including FIT). HTML output includes narrative placeholders and embedded report JSON for second-pass enrichment.
tp atp plans List ATP plans Compact list view
tp atp plan <atp-id> ATP detail for one plan Includes event metadata summary (count, next event name/date/priority/type). JSON mode also includes events[] with eventType, date, priority, race duration, and distance fields.
tp atp weeks --start YYYY-MM-DD --end YYYY-MM-DD ATP week range view Both flags are required
tp atp week [--week YYYY-MM-DD] ATP current-week summary Defaults to current week start
tp athlete settings [--cache-policy read-through|refresh|cache-only] Show athlete settings Default cache policy is read-through; use refresh to force API fetch or cache-only for strict offline mode. Human summary renders speed-zone threshold/min/max as pace (min/km). JSON output keeps raw speed fields and adds additive pace fields (thresholdPaceMinPerKm, minimumPaceMinPerKm, maximumPaceMinPerKm).
tp athlete readiness [--weeks N] [--type/--workout-type TYPE] Show current readiness and recent load context Includes CTL/ATL/TSB/ramp, current-week compliance vs ATP target, last-workout + 7-day EF, plus 7-day and 28-day aggregate load/efficiency metrics.
tp athlete performance [--start YYYY-MM-DD --end YYYY-MM-DD] [--days N] Performance trend summary for an arbitrary period vs previous equal-length period Use explicit --start/--end for fixed windows. If omitted, defaults to trailing --days window (default 28). Includes load/volume deltas plus power/pace EF trend and EF-by-run-type. Aggregation is execution-focused and uses completed workouts only.
tp athlete profile set-weight <kg> Store body weight for calorie estimation Body weight in kilograms (e.g. 72.5). This weight is used by the API for calorie estimation on future workouts.
tp athlete profile get-weight Show stored body weight Returns the currently stored body weight, or a message if none is set.

Workout structure JSON (for workouts create / workouts update / workouts estimate)

Quick reference: Which file for which command?

Command Flag File Content
workouts create --payload-file Full workout payload (metadata + structure)
workouts create --structure-file Structure-only (overrides structure field)
workouts update --payload-file Partial or full workout payload patch
workouts update --structure-file Structure-only patch (overrides payload structure)
workouts estimate --structure-file Structure-only
workouts estimate --payload-file Full payload (extracts structure field)

Full format details: See docs/api-payloads.md for complete JSON schemas, polyline format, and zone-to-target mappings.


Structure-only format (for --structure-file)

Used by workouts estimate and as structure override in workouts create / workouts update.

{
  "primaryIntensityMetric": "percentOfThresholdPace",
  "structure": [
    {
      "type": "step",
      "length": {"value": 1800, "unit": "second"},
      "steps": [
        {
          "name": "Active",
          "length": {"value": 1800, "unit": "second"},
          "targets": [{"minValue": 75, "maxValue": 85}],
          "intensityClass": "active",
          "openDuration": false
        }
      ]
    }
  ]
}

Example: Estimate a workout (structure-only)

# Create structure file
cat > run_structure.json << 'EOF'
{
  "primaryIntensityMetric": "percentOfThresholdPace",
  "structure": [
    {
      "type": "step",
      "length": {"value": 3600, "unit": "second"},
      "steps": [{"length": {"value": 3600, "unit": "second"}, "targets": [{"minValue": 80, "maxValue": 90}]}]
    }
  ]
}
EOF

# Estimate TSS/IF
uv run tp --json workouts estimate \
  --structure-file run_structure.json \
  --type run

Full payload format (for --payload-file with workouts create)

Includes workout metadata (workoutDay, title, etc.) plus structure.

End-to-end example: Create a workout for today

# 1. Create payload file (see docs/api-payloads.md for full schema)
cat > workout_today.json << 'EOF'
{
  "workoutDay": "2026-05-01T00:00:00",
  "workoutTypeValueId": 3,
  "title": "Z2 Run - 60 min",
  "publicSettingValue": 2,
  "structure": {
    "structure": [
      {
        "type": "step",
        "length": {"value": 1, "unit": "repetition"},
        "steps": [{
          "name": "Steady",
          "length": {"value": 3600, "unit": "second"},
          "targets": [{"minValue": 80, "maxValue": 90}],
          "intensityClass": "active",
          "openDuration": false
        }],
        "begin": 0,
        "end": 3600
      }
    ],
    "polyline": [[0,0],[0,0.85],[1,0.85],[1,0]],
    "primaryIntensityMetric": "percentOfThresholdPace",
    "primaryLengthMetric": "duration",
    "primaryIntensityTargetOrRange": "range"
  }
}
EOF

# 2. Create the workout
uv run tp --json workouts create \
  --day 2026-05-01 \
  --payload-file workout_today.json

Tip: workouts create and workouts update auto-estimate planned metrics by default. Use --no-auto-estimate to skip.


Supported intensity metrics (estimation path)

Common metrics supported by the estimator include:

  • percentOfThresholdPace
  • percentOfFtp
  • percentOfThresholdHr
  • percentOfMaxHr
  • rpe (approximation)

Distance estimation details:

  • percentOfThresholdPace: estimator can convert between time and distance with threshold pace.
  • percentOfFtp: estimator can also estimate distance for time-based steps using a linear heuristic when speed threshold is available (estimated speed = threshold speed * IF).

Common errors

  • Missing structure: provide --structure-file or include structure in --payload-file.
  • Missing workout type: provide --type (or workoutTypeValueId in payload).
  • No cached athlete settings: run uv run tp athlete settings --cache-policy refresh.
  • Invalid dates/JSON: ensure strict YYYY-MM-DD and valid JSON object payloads.

Common CLI workflows

Inspect auth and athlete context

uv run tp auth status --check
uv run tp auth whoami

Review workouts and workout details

# Current ISO week
uv run tp workouts list

# Explicit week/range
uv run tp workouts list --start 2026-05-04 --end 2026-05-10

# Summary and analytics for one workout
uv run tp workouts get <workout-id>
uv run tp workouts details <workout-id>

# FIT file analysis for completed workouts
uv run tp workouts analysis <workout-id>

# Deep-dive report (HTML default)
uv run tp workouts report <workout-id>

# JSON report to stdout
uv run tp workouts report <workout-id> --format json

Create a structured workout

cat > run_structure.json << 'EOF'
{
  "primaryIntensityMetric": "percentOfThresholdPace",
  "structure": [
    {
      "type": "step",
      "length": {"value": 3600, "unit": "second"},
      "steps": [
        {
          "name": "Steady",
          "length": {"value": 3600, "unit": "second"},
          "targets": [{"minValue": 80, "maxValue": 90}],
          "intensityClass": "active",
          "openDuration": false
        }
      ]
    }
  ]
}
EOF

# Preview estimated IF/TSS first
uv run tp --json workouts estimate \
  --structure-file run_structure.json \
  --type run

# Create once the estimate looks right
uv run tp --json workouts create \
  --structure-file run_structure.json \
  --day 2026-05-04 \
  --type run \
  --title "Z2 Run - 60 min"

Update or delete a workout

uv run tp --json workouts update <workout-id> \
  --title "Updated title"

# Persist post-activity comment via comments endpoint
uv run tp --json workouts comment <workout-id> \
  --text "Great durability today; keep progression controlled."

uv run tp workouts delete <workout-id> --yes

Check zones/settings, ATP, readiness, and performance

# Refresh zone/threshold settings used by estimation
uv run tp athlete settings --cache-policy refresh

# Logout (clear persisted auth)
uv run tp auth logout

# ATP plans and weekly targets
uv run tp atp plans
uv run tp atp plan <atp-id>
uv run tp atp week
uv run tp atp weeks --start 2026-05-04 --end 2026-05-31

# Current readiness and period trend
uv run tp athlete readiness
uv run tp athlete performance --days 28

# Body weight for calorie estimation
uv run tp athlete profile set-weight 72.5
uv run tp athlete profile get-weight

Athlete settings cache

  • Athlete settings are cached at ~/.tp/athlete-settings.json (per athlete).
  • Zone/threshold-dependent workflows now read cached settings by default.
  • Refresh cache explicitly when thresholds/zones change:
uv run tp athlete settings --cache-policy refresh

If cache is missing and a settings-dependent workflow is run, refresh first with the command above.

Dev commands

uv run pytest -q
uv run mypy src
uv run pre-commit run

Docs

Disclaimer

This project relies on reverse-engineered web API behavior and may break if TrainingPeaks changes endpoints or payloads.

License

MIT (see LICENSE).

About

No description, website, or topics provided.

Resources

License

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors