Unofficial CLI for inspecting TrainingPeaks data and creating/managing TrainingPeaks planned workouts via tpapi.trainingpeaks.com.
⚠️ Not affiliated with or supported by TrainingPeaks.
- 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 (viakeyring)
git clone <your-repo-url>
cd trainingpeaks-unofficial
uv syncuv 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--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
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": {}}}| 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. |
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.
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
}
]
}
]
}# 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 runIncludes 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.jsonTip:
workouts createandworkouts updateauto-estimate planned metrics by default. Use--no-auto-estimateto skip.
Common metrics supported by the estimator include:
percentOfThresholdPacepercentOfFtppercentOfThresholdHrpercentOfMaxHrrpe(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).
- Missing structure: provide
--structure-fileor includestructurein--payload-file. - Missing workout type: provide
--type(orworkoutTypeValueIdin payload). - No cached athlete settings: run
uv run tp athlete settings --cache-policy refresh. - Invalid dates/JSON: ensure strict
YYYY-MM-DDand valid JSON object payloads.
uv run tp auth status --check
uv run tp auth whoami# 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 jsoncat > 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"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# 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 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 refreshIf cache is missing and a settings-dependent workflow is run, refresh first with the command above.
uv run pytest -q
uv run mypy src
uv run pre-commit run- Python API usage:
docs/python-api.md - Workout/API payload formats:
docs/api-payloads.md - API mapping notes:
OFFICIAL_API_CROSSWALK.md - Architecture decisions:
docs/adr/
This project relies on reverse-engineered web API behavior and may break if TrainingPeaks changes endpoints or payloads.
MIT (see LICENSE).