Workouts
A workout is an ordered sequence of exercises and rest periods, with its own difficulty, calorie estimate, and duration.
List workouts
1GET /api/workouts/client| Parameter | Type | Default | Matches |
search | string | none | Partial, case-insensitive match on the English title |
category | string | none | Single value. Whole-string, case-insensitive match |
dif_level | string | none | Single value. Whole-string, case-insensitive match |
body_parts | string[] | none | Body part name, case-insensitive |
translation_languages | string[] | none | Only workouts translated into one of these language codes |
include_exercises | bool | false | true expands every workout with its full exercise sequence |
include_shared_library | bool | true | false returns only your company's own workouts |
lang | string | en | Language of the returned text |
limit | int | 10 | Page size, clamped to 100 |
offset | int | 0 | Rows to skip |
1curl "https://data.kinestex.com/api/workouts/client?category=Strength&dif_level=easy&body_parts=Chest,Triceps&limit=2" \
2 -H "x-api-key: YOUR_API_KEY" \
3 -H "Language: en"1{
2 "workouts": [
3 {
4 "id": 88,
5 "category": "Strength",
6 "calories": 250,
7 "type": "Upper Body",
8 "body_img_url": "https://firebasestorage.googleapis.com/.../body.png?alt=media",
9 "dif_level": "Easy",
10 "desc_img_url": "https://firebasestorage.googleapis.com/.../desc.png?alt=media",
11 "total_time": 1800,
12 "body_parts": ["Chest", "Triceps"],
13 "translation": {
14 "id": 201,
15 "workout_id": 88,
16 "language": "en",
17 "title": "Upper Body Starter",
18 "description": "A short push-focused session."
19 },
20 "translation_languages": ["en", "es", "de"],
21 "created_by": { "id": 1 },
22 "created_at": "2026-01-14T10:00:00Z",
23 "updated_at": "2026-02-02T08:30:00Z"
24 }
25 ],
26 "pagination": {
27 "total": 12,
28 "limit": 2,
29 "offset": 0,
30 "total_pages": 6,
31 "current_page": 1,
32 "has_next": true,
33 "has_prev": false
34 }
35}Workout fields (list view)
| Field | Type | Description |
id | number | Workout identifier |
category | string | Free-text category set per workout |
calories | number | Estimated calories burned |
type | string or null | Optional content type label |
dif_level | string | Difficulty as stored, usually capitalized |
body_img_url | string or null | Body-map image |
desc_img_url | string or null | Cover / preview image |
total_time | number | Duration in seconds |
body_parts | string[] | Targeted body parts |
translation | object or null | Localized title and description for the requested language |
translation_languages | string[] | Every language code this workout has |
workout_sequences | array | Present only with include_exercises=true |
created_by | object | { "id": <company id> } |
created_at / updated_at | string | ISO 8601 timestamps |
translation.title.translation is null when the requested language has no row for that workout, because this endpoint does not fall back to English. translation_languages always lists what does exist, so use it to decide whether to re-request in another language. Passing include_exercises=true switches to the detail builder, which does fall back to English.include_exercises=true adds a workout_sequences array to every item with full exercise and AI-model detail. It is a much larger payload: use it for a workout player, not for a browsing list.
Get one workout
1GET /api/workouts/client/{id}{id} accepts a numeric ID (88), a legacy 20-character Firestore ID, or a title (Upper Body Starter). Titles are normalized before matching, so spacing, hyphens, and casing do not matter. URL-encode spaces as %20.
| Parameter | Type | Default | Description |
lang | string | en | Language code, overrides the headers |
include_audio | bool | true | false omits generated coaching audio URLs |
This endpoint always returns the full exercise sequence. There is no include_exercises toggle on it.
1curl "https://data.kinestex.com/api/workouts/client/88?include_audio=false" \
2 -H "x-api-key: YOUR_API_KEY"1{
2 "id": 88,
3 "created_at": "2026-01-14T10:00:00Z",
4 "updated_at": "2026-02-02T08:30:00Z",
5 "category": "Strength",
6 "calories": 250,
7 "type": "Upper Body",
8 "dif_level": "Easy",
9 "desc_img_url": "https://firebasestorage.googleapis.com/.../desc.png?alt=media",
10 "total_time": 1800,
11 "total_minutes": 30,
12 "body_parts": ["Chest", "Triceps"],
13 "translation": {
14 "id": 201,
15 "language": "en",
16 "title": "Upper Body Starter",
17 "description": "A short push-focused session.",
18 "dif_level": "Easy"
19 },
20 "auto_calculate": false,
21 "turn_off_time_reminder": false,
22 "turn_off_sound_reminder": false,
23 "created_by": { "id": 1 },
24 "workout_sequences": [
25 {
26 "id": 100,
27 "workout_id": 88,
28 "exercise_id": 0,
29 "order": 1,
30 "countdown": 10,
31 "repeats": null,
32 "video_url": null,
33 "thumbnail_url": null,
34 "is_rest_sequence": true,
35 "created_at": "2026-01-14T10:00:00Z",
36 "updated_at": "2026-01-14T10:00:00Z"
37 },
38 {
39 "id": 101,
40 "workout_id": 88,
41 "exercise_id": 412,
42 "order": 2,
43 "countdown": null,
44 "repeats": 12,
45 "video_url": null,
46 "thumbnail_url": null,
47 "is_rest_sequence": false,
48 "created_at": "2026-01-14T10:00:00Z",
49 "updated_at": "2026-01-14T10:00:00Z",
50 "exercise": {
51 "id": "412",
52 "ai_model": { "id": "57" },
53 "repeats": 12,
54 "countdown": null,
55 "thumbnail_url": "https://firebasestorage.googleapis.com/.../thumb.png?alt=media",
56 "video_url": "https://firebasestorage.googleapis.com/.../video.mp4?alt=media",
57 "difficulty_level": "easy",
58 "position": "lying",
59 "calories_per_rep": 0.32,
60 "body_parts": ["Chest", "Triceps"],
61 "categories": ["Strength"],
62 "is_active": true,
63 "correct_second": 1.5,
64 "translation": {
65 "title": "Push Ups",
66 "language": "en",
67 "description": "A pushing movement for chest and triceps."
68 },
69 "created_by": { "id": 1 }
70 }
71 }
72 ]
73}Detail-only fields
| Field | Type | Description |
total_minutes | number | total_time divided by 60, for display |
auto_calculate | bool | Calories are recomputed from the exercise list |
turn_off_time_reminder | bool | Time reminders disabled for this workout |
turn_off_sound_reminder | bool | Sound reminders disabled for this workout |
workout_sequences | array | Ordered entries, see below |
Workout sequence entry
| Field | Type | Description |
order | number | Position in the workout, ascending |
is_rest_sequence | bool | true for a rest block; exercise is then absent |
countdown | number or null | Timer length in seconds, for timed entries and rests |
repeats | number or null | Repetition target, for rep-based entries |
video_url / thumbnail_url | string or null | Per-sequence overrides of the exercise assets |
exercise | object | Full exercise detail, same shape as GET /api/exercises/client/{id} |
exercise_id | number | 0 on rest entries |
translation | object | Rest-period speech text and audio, when the sequence has one |
Not found returns 404 { "error": "Workout not found" }.