workoutx-sdk — WorkoutX Fitness & Exercise API SDK for Python
The official Python SDK for the WorkoutX API — 1,300+ exercises with form GIFs, a workout generator, supplement intelligence, and AI body-scan analysis, all behind one simple key. A modern Python ExerciseDB alternative for fitness apps.
🔑 Get a free API key · 🌐 workoutxapp.com · 📚 API docs
Covers exercises, GIFs, workout generator, supplements, Body Scan, account, and billing
in one client. Python 3.8+. Only depends on certifi (for reliable TLS across platforms).
Install
pip install workoutx-sdk
Quick start
from workoutx import WorkoutX
wx = WorkoutX(
api_key="wx_your_key_here", # exercises / gifs / workout / supplements
scan_token="eyJ...", # body scan (optional)
)
# Exercises
page = wx.exercises.list(limit=20)
by_id = wx.exercises.get("0001")
lunges = wx.exercises.by_name("lunges")
similar = wx.exercises.similar("0001")
alts = wx.exercises.alternatives("0001", equipment="dumbbell")
# GIFs
data = wx.gifs.get("0001") # bytes
src = wx.gif_url("0001") # URL for <img>
# Workout & supplements
workout = wx.workout.generate(goal="hypertrophy", days=4)
stack = wx.supplements.stack(goal="cut")
# Body Scan
credits = wx.scan.credits()
history = wx.scan.history(limit=10)
# Account & billing
login = wx.auth.login("u@x.com", "•••") # token cached for scan/auth/billing
me = wx.auth.me()
keys = wx.auth.list_keys()
sub = wx.billing.status()
checkout = wx.billing.checkout("pro", "yearly") # -> {"url": ...} to redirect to Stripe
Authentication
| Product | Resources | Credential |
|---|---|---|
| Exercises | exercises, gifs, workout, supplements |
api_key (wx_...) |
| Body Scan / Account / Billing | scan.*, auth.*, billing.* |
Bearer JWT |
auth, scan, and billing share the same Bearer token — a successful
auth.login() caches it, so later scan.* / billing.* calls work without
re-supplying credentials. Google OAuth (/v1/auth/google) is a browser-redirect
flow and is intentionally not wrapped.
For Body Scan, pass a ready token or let the SDK log in for you:
WorkoutX(api_key=key, scan_token="eyJ...") # ready token
WorkoutX(api_key=key, scan_email="u@x.com", scan_password="•") # auto-login
Avoiding the "name as ID" 404
exercises.get(id) needs an exact numeric ID (IDs have gaps). For names, use
by_name, or the resolver that tries an ID then falls back to a name search:
ex = wx.exercises.find("lunges") # dict | None
API v2
wx.v2 is the new exercise catalog: 1,900+ movements as 2,700+ clips (male and
female models are separate exercises, linked by counterpartId), with 720p MP4
media at 30 fps. The API key is always sent in the X-WorkoutX-Key header.
Media links are signed and expire after about 24 hours: store exercise ids,
never media URLs, and call fresh_media(id) when you display an exercise. It
reuses the links of every exercise the SDK has already received (from list,
get, similar, alternatives, workout.generate and workout.program)
until they are close to expiry, and only then makes a request. Parameters keep
the API's camelCase names (weightKg, bodyFocus, avoidJoints, daysPerWeek).
# Catalog. lang: en, tr, de, es, fr, zh-SG, zh-HK (filters stay English)
page = wx.v2.list(body_part="chest", model="female", lang="de")
bench = wx.v2.get("ex_SAWOOH", lang="tr") # Barbell Bench Press
from_v1 = wx.v2.legacy("0025") # v1 id -> v2 exercise
# Similar exercises, alternatives and calories
similar = wx.v2.similar("ex_SAWOOH", limit=5) # {exerciseId, model, total, limit, offset, results}
for ex in similar["results"]:
print(ex["name"], ex["score"]) # full v2 exercises, best first
alts = wx.v2.alternatives("ex_SAWOOH", equipment="dumbbell", model="any")
kcal = wx.v2.calories("ex_SAWOOH", weightKg=80, minutes=20) # kcal["totalKcal"]
# Workout sessions (lists are sent comma-separated)
session = wx.v2.workout.generate(
goal="muscle_gain", duration=45, equipment="home", # or ["dumbbell", "bench"]; presets gym, home, bodyweight
split="upper", avoidJoints=["shoulder"], format="superset", model="female",
)
for step in session["warmup"]["items"]:
print(step["exercise"]["name"], step["durationSeconds"])
for x in session["exercises"]:
# block "C1" + "C2" = a superset; role: main, secondary, accessory, core, conditioning, mobility
links = wx.v2.fresh_media(x["exercise"]["id"]) # no request: cached from generate()
print(x["block"], x["exercise"]["name"], x["sets"], x.get("reps") or f'{x["durationSeconds"]}s',
x.get("rir"), x["cue"], links and links["mp4"])
# Multi-week programs: exercises reference exerciseLibrary by id
program = wx.v2.workout.program(goal="strength", weeks=12, daysPerWeek=4, seed="plan-1")
library = {e["id"]: e for e in program["exerciseLibrary"]}
for week in program["program"]:
for day in week["days"]:
for x in day["exercises"]:
print(week["week"], week["phase"], day["weekday"], library[x["exerciseId"]]["name"], x["sets"], x.get("reps"))
# Supplements (take v2 exercise ids)
supps = wx.v2.supplements.list(goal="strength", evidence="strong")
picks = wx.v2.supplements.for_exercise("ex_SAWOOH", limit=3)
stack = wx.v2.supplements.stack(goal="muscle_gain", level="essential")
A session (workout.generate) has a summary, a warmup (with rampUp sets
before the first main lift), exercises and a cooldown. Each exercise has a
block ("A", "B", "C1"/"C2" for a superset), role, movement pattern,
sets, reps or durationSeconds, restSeconds, rir, tempo, load, a
form cue, a rationale and light alternatives (no media: wx.v2.get(id)
them when the user swaps). Circuits add circuit timing and rounds. goal
takes muscle_gain, strength, fat_loss, endurance, mobility or
general_fitness; duration is 15 to 120 minutes; avoidJoints takes knee,
shoulder, lumbar_spine, elbow, wrist, hip, ankle; format is
auto, straight, superset or circuit.
A program (workout.program, 2 to 16 weeks, seed for a repeatable plan)
has a schedule, a progressionModel, blocks with deload weeks,
weeklyVolume per muscle group, the session templates (sessions) and the
weeks (program). Their exercises are light references by exerciseId; each
exercise appears once, with media, in exerciseLibrary, and fresh_media
reuses those links. Programs take no split, bodyFocus or exclude.
| Feature | Plans |
|---|---|
similar, alternatives, calories |
all plans |
Joint & Rehab filters on list (jointFocus, intensityLevel, movementTag) |
Growth, Scale (legacy Ultra) |
workout.generate, supplements.* except stack |
Growth, Scale (legacy Pro, Ultra) |
workout.program, supplements.stack |
Scale (legacy Ultra) |
On a plan without the feature, the call raises WorkoutXError with status
403; e.body["requiredPlan"] names the cheapest plan that has it.
Error handling
from workoutx import WorkoutXError
try:
wx.exercises.get("nope")
except WorkoutXError as e:
e.status # 404
e.code # "Not Found"
e.tip # hint from API, when present
e.is_not_found # plus is_auth_error, is_rate_limited
Transient failures (429, 5xx, network) are retried automatically with backoff,
honoring Retry-After.
Development
pip install -e ".[test]"
pytest # 44 offline unit tests (no network)
About WorkoutX
WorkoutX is a developer-first fitness API — a modern ExerciseDB alternative that bundles everything you need to build workout, training, and health apps behind a single key:
- 🏋️ 1,300+ exercises with animated form GIFs, target & secondary muscles, equipment, and step-by-step instructions
- 🧠 Workout generator and multi-week training programs
- 💊 Supplement intelligence — recommended stacks per goal and per exercise
- 📸 AI Body Scan — body-fat %, measurements, and composition from photos
- 🔌 Clean REST API with official SDKs for JavaScript, Python, Flutter, and Swift
Links
- 🌐 Website — https://workoutxapp.com
- 🔑 Get a free API key — https://workoutxapp.com/dashboard.html
- 📚 API documentation — https://workoutxapp.com/docs.html
- 🆚 ExerciseDB alternative — https://workoutxapp.com/exercisedb-alternative.html
Official SDKs
- JavaScript / TypeScript —
@workoutx/sdk(npm) - Python —
workoutx-sdk(PyPI) - Flutter / Dart —
workoutx_sdk(pub.dev) - Swift — workoutx-sdk-swift (Swift Package Manager)
License
MIT
Metadata
Release files for workoutx-sdk 0.2.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 | Provenance | |
|---|---|---|---|---|
| workoutx_sdk-0.2.0.tar.gz | 25.7 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| workoutx_sdk-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 46.6 kB
Release files / workoutx_sdk-0.2.0.tar.gz
| Download URL | workoutx_sdk-0.2.0.tar.gz |
|---|---|
| Size | 25.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
65b60f4a863ac415fa28595afb9a51044c521a3c9e0d14110b42510aa8f29f5e
|
|
BLAKE2b-256 checksum How to use checksums |
8a167bc4c1b393a159a56c799ba36b80e1fe4855812a1bf3b98bdb551190ff9e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Release files / workoutx_sdk-0.2.0-py3-none-any.whl
| Download URL | workoutx_sdk-0.2.0-py3-none-any.whl |
|---|---|
| Size | 20.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
d7b0c9f9e76201554fcfea657b9007cb192d0d7c85cb72f7aae69090a64cfcd8
|
|
BLAKE2b-256 checksum How to use checksums |
6e6d568832ca3b5d4d05d75b1c69eec6396065a2dfac1d826424612d9873d0bb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.13.5
|
Security for workoutx-sdk 0.2.0
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.