Catalogs
Exercises, muscles, metrics, prescription types and units: the reference data that ids in other resources point to. Your organization's own exercises can also be created, updated and deleted.
Catalogs change rarely. Load them once, cache them, and resolve ids locally instead of calling a detail endpoint per row.
Exercises are the one catalog you can write: your organization's own exercises, never Vitruve's library (organizationId: null), which answers 403. The writes need your organization's own API key and follow the write rules.
List exercises
Vitruve’s global exercise library plus the exercises your organization created, ordered by name. Each exercise carries its primary and secondary muscles and your organization’s exerciseMetadata.
Request
curl "https://api-next.vitruve.fit/v1/exercises?limit=2&search=squat&source=global" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/exercises?limit=2&search=squat&source=global',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/exercises?limit=2&search=squat&source=global",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"name": "Back Squat",
"organizationId": null,
"isMain": true,
"isJump": false,
"isReaction": false,
"isSprint": false,
"equipment": "barbell",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"instructions": "Set the bar across your upper back and unrack it with feet shoulder-width apart. Bend at the hips and knees until the thighs are at least parallel, then drive up through the whole foot to stand.",
"category": "strength",
"youtubeUrl": "https://www.youtube.com/results?search_query=how%20to%20do%20Back%20Squat",
"googleUrl": "https://www.google.com/search?q=how%20to%20do%20Back%20Squat",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null,
"primaryMuscles": [
{
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"secondaryMuscles": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
},
{
"id": "e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
"name": "hamstrings",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"exerciseMetadata": [
{
"id": "9d5c2a71-e4f8-4b36-8c0d-6a1e3f7b9c52",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"videoUrl": "https://videos.northgate.example/back-squat.mp4",
"createdAt": "2025-10-03T11:12:40.000Z",
"updatedAt": "2025-10-03T11:12:40.000Z"
}
]
}
],
"meta": {
"limit": 2,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
searchstring- Case-insensitive match on the exercise name.
categorystring- Exact, case-sensitive match on
category, e.g.strengthorplyometrics. sourceall | global | organizationdefaultallglobalreturns only Vitruve’s library,organizationonly your own exercises,allboth.limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Global exercises have
organizationId: null. Other organizations’ exercises are never returned. - Deleted muscles are left out of
primaryMusclesandsecondaryMuscles. - Catalogs change rarely. Cache them and refresh once a day rather than resolving ids one request at a time.
Response fields
iduuid- Exercise id. Matches
exerciseIdon prescriptions, metric values, equations and leaderboards. namestring- Exercise name.
organizationIduuidnullablenullfor Vitruve’s global library; your organization id for exercises your coaches created.isMainbooleannullable- Whether the exercise is flagged as a main lift.
isJumpbooleannullable- Jump exercise.
isReactionandisSprintflag reaction and sprint exercises the same way. categorystringnullable- Free-text category. The global library uses
strength,plyometrics,stretching,powerlifting,olympic weightlifting,cardio,strongmanandtest. equipmentstringnullable- Equipment, e.g.
barbell.force,levelandmechanicare free-text descriptors too. instructionsstringnullable- How to perform the exercise.
youtubeUrlstringnullable- Search link for demonstration videos.
googleUrlis the same for a web search. primaryMuscles[]object[]- Main muscles worked, as full muscle objects (see
GET /muscles). secondaryMuscles[]object[]- Supporting muscles, same shape.
exerciseMetadata[]object[]- Your organization’s custom video for the exercise, if one was set. At most one entry; other organizations’ metadata is never included.
exerciseMetadata[].videoUrlstring- Video URL set by your organization.
Failures are described in error codes.
Get an exercise
One global exercise or one of your organization’s exercises, with its muscles and your organization’s exerciseMetadata.
Request
curl "https://api-next.vitruve.fit/v1/exercises/1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/exercises/1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/exercises/1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"name": "Back Squat",
"organizationId": null,
"isMain": true,
"isJump": false,
"isReaction": false,
"isSprint": false,
"equipment": "barbell",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"instructions": "Set the bar across your upper back and unrack it with feet shoulder-width apart. Bend at the hips and knees until the thighs are at least parallel, then drive up through the whole foot to stand.",
"category": "strength",
"youtubeUrl": "https://www.youtube.com/results?search_query=how%20to%20do%20Back%20Squat",
"googleUrl": "https://www.google.com/search?q=how%20to%20do%20Back%20Squat",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null,
"primaryMuscles": [
{
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"secondaryMuscles": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
},
{
"id": "e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
"name": "hamstrings",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"exerciseMetadata": [
{
"id": "9d5c2a71-e4f8-4b36-8c0d-6a1e3f7b9c52",
"exerciseId": "1c7a5e93-f804-4b26-a9d1-3e6b0d82c4f7",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"videoUrl": "https://videos.northgate.example/back-squat.mp4",
"createdAt": "2025-10-03T11:12:40.000Z",
"updatedAt": "2025-10-03T11:12:40.000Z"
}
]
}
}Path parameters
iduuidrequired- Resource id.
Notes
- An exercise owned by another organization returns
404, as does a deleted one.
Response fields
iduuid- Exercise id. Matches
exerciseIdon prescriptions, metric values, equations and leaderboards. namestring- Exercise name.
organizationIduuidnullablenullfor Vitruve’s global library; your organization id for exercises your coaches created.isMainbooleannullable- Whether the exercise is flagged as a main lift.
isJumpbooleannullable- Jump exercise.
isReactionandisSprintflag reaction and sprint exercises the same way. categorystringnullable- Free-text category. The global library uses
strength,plyometrics,stretching,powerlifting,olympic weightlifting,cardio,strongmanandtest. equipmentstringnullable- Equipment, e.g.
barbell.force,levelandmechanicare free-text descriptors too. instructionsstringnullable- How to perform the exercise.
youtubeUrlstringnullable- Search link for demonstration videos.
googleUrlis the same for a web search. primaryMuscles[]object[]- Main muscles worked, as full muscle objects (see
GET /muscles). secondaryMuscles[]object[]- Supporting muscles, same shape.
exerciseMetadata[]object[]- Your organization’s custom video for the exercise, if one was set. At most one entry; other organizations’ metadata is never included.
exerciseMetadata[].videoUrlstring- Video URL set by your organization.
Failures are described in error codes.
Create an exercise
Creates an exercise for your organization and answers it in the shape of GET /exercises/{id}.
Request
curl -X POST "https://api-next.vitruve.fit/v1/exercises" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: YOUR_OPERATION_ID" \
-d '{
"name": "Safety Bar Squat",
"category": "strength",
"equipment": "safety squat bar",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"isMain": true,
"primaryMuscleIds": [
"5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52"
],
"secondaryMuscleIds": [
"2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264"
]
}'const response = await fetch(
'https://api-next.vitruve.fit/v1/exercises',
{
method: 'POST',
headers: {
'X-API-Key': process.env.VITRUVE_API_KEY,
'Content-Type': 'application/json',
'Idempotency-Key': 'YOUR_OPERATION_ID'
},
body: JSON.stringify({
name: 'Safety Bar Squat',
category: 'strength',
equipment: 'safety squat bar',
force: 'push',
level: 'intermediate',
mechanic: 'compound',
isMain: true,
primaryMuscleIds: [
'5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52'
],
secondaryMuscleIds: [
'2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264'
]
})
}
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.request(
"POST",
"https://api-next.vitruve.fit/v1/exercises",
headers={
"X-API-Key": os.environ["VITRUVE_API_KEY"],
"Idempotency-Key": "YOUR_OPERATION_ID",
},
json={
"name": "Safety Bar Squat",
"category": "strength",
"equipment": "safety squat bar",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"isMain": True,
"primaryMuscleIds": [
"5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
],
"secondaryMuscleIds": [
"2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
],
},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926",
"name": "Safety Bar Squat",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"isMain": true,
"isJump": false,
"isReaction": false,
"isSprint": false,
"equipment": "safety squat bar",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"instructions": null,
"category": "strength",
"youtubeUrl": null,
"googleUrl": null,
"updatedAt": "2026-10-13T09:40:02.318Z",
"createdAt": "2026-10-13T09:40:02.318Z",
"deletedAt": null,
"primaryMuscles": [
{
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"secondaryMuscles": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"exerciseMetadata": []
}
}Headers
Idempotency-Keystring- Request header. 1 to 255 visible ASCII characters you choose per operation, such as a UUID or your own record’s id. A retry with the same key and the same request returns the first answer instead of creating again.
Body
namestringrequired- Exercise name, up to 255 characters.
categorystringnullable- Free-text category, up to 255 characters, e.g.
strength. equipmentstringnullable- Equipment, up to 255 characters, e.g.
barbell. forcepull | push | staticnullable- Force type.
levelbeginner | intermediate | expertnullable- Difficulty.
mechaniccompound | isolationnullable- Mechanic.
instructionsstringnullable- How to perform the exercise, up to 10,000 characters.
isMainboolean- Flags the exercise as a main lift.
isJumpboolean- Jump exercise. At most one of
isJump,isReactionandisSprintistrue. isReactionboolean- Reaction exercise. Needs timing gates in the plan.
isSprintboolean- Sprint exercise. Needs timing gates in the plan.
youtubeUrlstringnullable- Link to a demonstration video: an
httporhttpsURL, up to 255 characters. googleUrlstringnullable- Web link: an
httporhttpsURL, up to 255 characters. primaryMuscleIdsuuid[]- Main muscles worked, at most 50. See
GET /muscles. secondaryMuscleIdsuuid[]- Supporting muscles, at most 50.
Notes
- More than one of
isJump,isReactionandisSprintset totrueanswers400. - A sprint or reaction exercise answers
403when the plan doesn’t include timing gates. 400when a muscle id is unknown.- Send an
Idempotency-Keyheader to retry the create safely after a timeout: a retry with the same key and body answers the first result instead of creating again.
Failures are described in error codes.
Update an exercise
Request body: any of the create fields, at least one. A muscle list replaces the current one; a list you leave out is kept.
Changes only the fields sent, and answers the updated exercise. null clears an optional field.
Request
curl -X PATCH "https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926" \
-H "X-API-Key: YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"instructions": "Hold the handles in front of you with the pads on your upper back. Squat to depth and stand up tall.",
"secondaryMuscleIds": [
"2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035"
]
}'const response = await fetch(
'https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926',
{
method: 'PATCH',
headers: {
'X-API-Key': process.env.VITRUVE_API_KEY,
'Content-Type': 'application/json'
},
body: JSON.stringify({
instructions: 'Hold the handles in front of you with the pads on your upper back. Squat to depth and stand up tall.',
secondaryMuscleIds: [
'2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264',
'e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035'
]
})
}
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.request(
"PATCH",
"https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
json={
"instructions": "Hold the handles in front of you with the pads on your upper back. Squat to depth and stand up tall.",
"secondaryMuscleIds": [
"2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
],
},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926",
"name": "Safety Bar Squat",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"isMain": true,
"isJump": false,
"isReaction": false,
"isSprint": false,
"equipment": "safety squat bar",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"instructions": "Hold the handles in front of you with the pads on your upper back. Squat to depth and stand up tall.",
"category": "strength",
"youtubeUrl": null,
"googleUrl": null,
"updatedAt": "2026-10-14T07:12:45.903Z",
"createdAt": "2026-10-13T09:40:02.318Z",
"deletedAt": null,
"primaryMuscles": [
{
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"secondaryMuscles": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
},
{
"id": "e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
"name": "hamstrings",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"exerciseMetadata": []
}
}Path parameters
iduuidrequired- Resource id.
Body
namestring- Exercise name, up to 255 characters.
categorystringnullable- Free-text category, up to 255 characters, e.g.
strength. equipmentstringnullable- Equipment, up to 255 characters, e.g.
barbell. forcepull | push | staticnullable- Force type.
levelbeginner | intermediate | expertnullable- Difficulty.
mechaniccompound | isolationnullable- Mechanic.
instructionsstringnullable- How to perform the exercise, up to 10,000 characters.
isMainboolean- Flags the exercise as a main lift.
isJumpboolean- Jump exercise. At most one of
isJump,isReactionandisSprintistrue. isReactionboolean- Reaction exercise. Needs timing gates in the plan.
isSprintboolean- Sprint exercise. Needs timing gates in the plan.
youtubeUrlstringnullable- Link to a demonstration video: an
httporhttpsURL, up to 255 characters. googleUrlstringnullable- Web link: an
httporhttpsURL, up to 255 characters. primaryMuscleIdsuuid[]- Main muscles worked, at most 50. See
GET /muscles. Replaces the current list. secondaryMuscleIdsuuid[]- Supporting muscles, at most 50. Replaces the current list.
Notes
- Only your organization’s exercises: one of Vitruve’s library (
organizationId: null) answers403. - The modality rules of the create apply to the exercise as updated.
Failures are described in error codes.
Delete an exercise
Archives an exercise and answers it with deletedAt set. Workouts that used it keep showing it.
Request
curl -X DELETE "https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926',
{
method: 'DELETE',
headers: {
'X-API-Key': process.env.VITRUVE_API_KEY
}
}
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.request(
"DELETE",
"https://api-next.vitruve.fit/v1/exercises/2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "2c9e5a71-d083-4b6f-a1e4-58f0b7c3d926",
"name": "Safety Bar Squat",
"organizationId": "7d3f8a52-1c0e-4b8e-9f4a-2e6b1d9c5a10",
"isMain": true,
"isJump": false,
"isReaction": false,
"isSprint": false,
"equipment": "safety squat bar",
"force": "push",
"level": "intermediate",
"mechanic": "compound",
"instructions": "Hold the handles in front of you with the pads on your upper back. Squat to depth and stand up tall.",
"category": "strength",
"youtubeUrl": null,
"googleUrl": null,
"updatedAt": "2026-10-14T08:02:19.640Z",
"createdAt": "2026-10-13T09:40:02.318Z",
"deletedAt": "2026-10-14T08:02:19.640Z",
"primaryMuscles": [
{
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"secondaryMuscles": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
},
{
"id": "e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
"name": "hamstrings",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"exerciseMetadata": []
}
}Path parameters
iduuidrequired- Resource id.
Notes
- Only your organization’s exercises: one of Vitruve’s library (
organizationId: null) answers403. - Once deleted, the exercise leaves
GET /exercisesandGET /exercises/{id}answers404.
Failures are described in error codes.
List muscles
Every muscle exercises can reference (see primaryMuscleIds / secondaryMuscleIds), ordered by name. Names are lowercase, e.g. quadriceps, lower back.
Request
curl "https://api-next.vitruve.fit/v1/muscles?limit=2" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/muscles?limit=2',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/muscles?limit=2",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "2b7e4f19-c6a3-4d80-9e51-a8d3f0c7b264",
"name": "glutes",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
},
{
"id": "e6a09c3d-5f72-4b18-8d4e-17c9b2a6f035",
"name": "hamstrings",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
],
"meta": {
"limit": 2,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Catalogs change rarely. Cache them and refresh once a day rather than resolving ids one request at a time.
Response fields
iduuid- Muscle id. Matches the ids in an exercise’s
primaryMusclesandsecondaryMuscles. namestring- Lowercase muscle name.
Failures are described in error codes.
Get a muscle
One muscle.
Request
curl "https://api-next.vitruve.fit/v1/muscles/5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/muscles/5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/muscles/5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "5c8f2d06-a931-4e7b-96d4-0b3e8a1f7c52",
"name": "quadriceps",
"updatedAt": "2026-05-04T07:30:00.000Z",
"createdAt": "2024-01-15T09:00:00.000Z",
"deletedAt": null
}
}Path parameters
iduuidrequired- Resource id.
Failures are described in error codes.
List metrics
Metric definitions, ordered by order. Use them to name and unit the values inside workouts.
Request
curl "https://api-next.vitruve.fit/v1/metrics?limit=1&group=Velocity&isRepetitionMetric=true" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/metrics?limit=1&group=Velocity&isRepetitionMetric=true',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/metrics?limit=1&group=Velocity&isRepetitionMetric=true",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"name": "Mean Propulsive Velocity",
"key": "mean-propulsive-velocity",
"group": "Velocity",
"dimension": null,
"unitId": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"order": 0,
"isMainVelocityMetric": true,
"isRepetitionMetric": true,
"isSerieMetric": false,
"isQuickStartMetric": true,
"isEquationMetric": true,
"isUserMetric": false,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null
}
],
"meta": {
"limit": 1,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
groupGeneral | Weight | Time | Distance | Velocity | Fatigue | Percentage | Power | Force | Acceleration | Scale | Heart Rate | Temperature | Pressure | Energy | Volume | Pace | Oxygen Consumption | Concentration- Only metrics in this group. Exact, case-sensitive match.
isRepetitionMetricboolean- Only metrics recorded per repetition (
true) or not (false). isSerieMetricboolean- Only metrics recorded per set (
true) or not (false). isUserMetricboolean- Only athlete metrics such as body weight (
true) or not (false). isEquationMetricboolean- Only metrics usable in equations (
true) or not (false). limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- Boolean filters accept only
trueorfalse; any other value is a400. - Metrics are global: every organization sees the same set.
- Catalogs change rarely. Cache them and refresh once a day rather than resolving ids one request at a time.
Response fields
iduuid- Metric id. Matches
metricIdon metric values, equations and leaderboards. namestring- Display name.
keystring- Stable slug for the metric, e.g.
mean-propulsive-velocity. groupstring- What the metric measures, e.g.
Velocity,Power,Fatigue. dimensionstringnullable- Legacy column superseded by
group. Do not rely on it. unitIduuid- Unit the metric’s values are stored in. Resolve with
GET /units/{id}. orderinteger- Display order within the group, lowest first. Lists are sorted by it.
isMainVelocityMetricboolean- The primary velocity read of a VBT set.
isRepetitionMetricboolean- Values are recorded per repetition.
isSerieMetricboolean- Values are recorded per set.
isUserMetricboolean- Values belong to the athlete rather than a session, e.g. body weight.
isEquationMetricboolean- The metric can be used in equations (see
GET /equations). isQuickStartMetricboolean- Shown by default in the app’s quick-start mode.
Failures are described in error codes.
Get a metric
One metric definition.
Request
curl "https://api-next.vitruve.fit/v1/metrics/d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/metrics/d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/metrics/d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"name": "Mean Propulsive Velocity",
"key": "mean-propulsive-velocity",
"group": "Velocity",
"dimension": null,
"unitId": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"order": 0,
"isMainVelocityMetric": true,
"isRepetitionMetric": true,
"isSerieMetric": false,
"isQuickStartMetric": true,
"isEquationMetric": true,
"isUserMetric": false,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null
}
}Path parameters
iduuidrequired- Resource id.
Response fields
iduuid- Metric id. Matches
metricIdon metric values, equations and leaderboards. namestring- Display name.
keystring- Stable slug for the metric, e.g.
mean-propulsive-velocity. groupstring- What the metric measures, e.g.
Velocity,Power,Fatigue. dimensionstringnullable- Legacy column superseded by
group. Do not rely on it. unitIduuid- Unit the metric’s values are stored in. Resolve with
GET /units/{id}. orderinteger- Display order within the group, lowest first. Lists are sorted by it.
isMainVelocityMetricboolean- The primary velocity read of a VBT set.
isRepetitionMetricboolean- Values are recorded per repetition.
isSerieMetricboolean- Values are recorded per set.
isUserMetricboolean- Values belong to the athlete rather than a session, e.g. body weight.
isEquationMetricboolean- The metric can be used in equations (see
GET /equations). isQuickStartMetricboolean- Shown by default in the app’s quick-start mode.
Failures are described in error codes.
List prescription types
The parameters a coach can prescribe for a set (weight, repetitions, velocity targets, fatigue limits…), ordered by name. Use them to read seriePrescriptions in workouts, programs and templates.
Request
curl "https://api-next.vitruve.fit/v1/prescription-types?limit=2" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/prescription-types?limit=2',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/prescription-types?limit=2",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "b8d24f6a-0c97-4e35-a1f8-5d3e9c7b2a16",
"name": "Fatigue [MPV]",
"label": "Fatigue [MPV]",
"metricId": "4c9e7b20-d1a5-4f83-b6e2-a07f3d58c914",
"primaryMetricId": "d06b3f81-4e92-4a7c-8c15-b7e29a0d5f63",
"group": "VBT",
"prescriptionGroup": null,
"trainingModality": "vbt",
"valueType": "int",
"placeholder": "0",
"formatRegex": "^(100|\\d{0,2})?$",
"hasRange": false,
"hasTopMode": false,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null
},
{
"id": "3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05",
"name": "Weight",
"label": "Weight",
"metricId": "62f0d8a4-b3c9-4e17-8a5d-0e4c7f1b9d28",
"primaryMetricId": null,
"group": "General",
"prescriptionGroup": null,
"trainingModality": null,
"valueType": "float",
"placeholder": "0",
"formatRegex": "^\\d{0,5}(\\.\\d{0,2})?$",
"hasRange": true,
"hasTopMode": false,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null
}
],
"meta": {
"limit": 2,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
groupGeneral | VBT | Velocity | Jump | Sprint | Reaction | Time | Test | Other- Only prescription types in this group. Exact, case-sensitive match.
trainingModalityvbt | jump | sprint | reaction- Only prescription types for this modality. Types that apply to any modality have none and are left out.
limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- The units a prescription type can be entered in are not included in the response.
- Catalogs change rarely. Cache them and refresh once a day rather than resolving ids one request at a time.
Response fields
iduuid- Prescription type id. Matches
prescriptionTypeIdon a workout’sseriePrescriptions. namestring- Name of the parameter, e.g.
Weight,Fatigue [MPV]. labelstring- Short label shown next to the value.
groupstring- Grouping used in the app, e.g.
General,VBT,Jump. prescriptionGroupstringnullable- Legacy column superseded by
group. Do not rely on it. trainingModalitystringnullablevbt,jump,sprintorreaction.nullfor parameters that apply to any modality, such as weight.metricIduuidnullable- Metric the prescribed value is compared against. Resolve with
GET /metrics/{id}. primaryMetricIduuidnullable- Underlying repetition metric, when the parameter is derived from one (e.g. a fatigue target built on Mean Propulsive Velocity).
valueTypestring- How the value is entered:
float,int,time,tempoorselect. formatRegexstring- Pattern the app validates entered values against.
hasRangeboolean- Whether a coach can prescribe a min–max range instead of one value.
hasTopModeboolean- Whether the parameter supports “top set” mode.
Failures are described in error codes.
Get a prescription type
One prescription type.
Request
curl "https://api-next.vitruve.fit/v1/prescription-types/3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/prescription-types/3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/prescription-types/3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "3e9d7a12-c5b8-4f40-a136-d82e4b6f9c05",
"name": "Weight",
"label": "Weight",
"metricId": "62f0d8a4-b3c9-4e17-8a5d-0e4c7f1b9d28",
"primaryMetricId": null,
"group": "General",
"prescriptionGroup": null,
"trainingModality": null,
"valueType": "float",
"placeholder": "0",
"formatRegex": "^\\d{0,5}(\\.\\d{0,2})?$",
"hasRange": true,
"hasTopMode": false,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null
}
}Path parameters
iduuidrequired- Resource id.
Response fields
iduuid- Prescription type id. Matches
prescriptionTypeIdon a workout’sseriePrescriptions. namestring- Name of the parameter, e.g.
Weight,Fatigue [MPV]. labelstring- Short label shown next to the value.
groupstring- Grouping used in the app, e.g.
General,VBT,Jump. prescriptionGroupstringnullable- Legacy column superseded by
group. Do not rely on it. trainingModalitystringnullablevbt,jump,sprintorreaction.nullfor parameters that apply to any modality, such as weight.metricIduuidnullable- Metric the prescribed value is compared against. Resolve with
GET /metrics/{id}. primaryMetricIduuidnullable- Underlying repetition metric, when the parameter is derived from one (e.g. a fatigue target built on Mean Propulsive Velocity).
valueTypestring- How the value is entered:
float,int,time,tempoorselect. formatRegexstring- Pattern the app validates entered values against.
hasRangeboolean- Whether a coach can prescribe a min–max range instead of one value.
hasTopModeboolean- Whether the parameter supports “top set” mode.
Failures are described in error codes.
List units
Units of measure, ordered by name. Each unit converts to its base unit with conversionFactor: base = value × conversionFactor. For pounds the factor is 0.45359237, so 100 lb is 45.359237 kg.
Request
curl "https://api-next.vitruve.fit/v1/units?dimension=Weight" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/units?dimension=Weight',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/units?dimension=Weight",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": [
{
"id": "09e4b7a3-d2c6-4158-b8f0-a71d5c3e9246",
"name": "Kilograms",
"dimension": "Weight",
"measurementSystem": "metric",
"label": "kg",
"baseUnitId": "09e4b7a3-d2c6-4158-b8f0-a71d5c3e9246",
"pairUnitId": "7b13e9c5-48d2-4a06-b9f1-c2e8a5d4f317",
"conversionFactor": 1,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null,
"prescriptionTypeId": null
},
{
"id": "7b13e9c5-48d2-4a06-b9f1-c2e8a5d4f317",
"name": "Pounds",
"dimension": "Weight",
"measurementSystem": "imperial",
"label": "lb",
"baseUnitId": "09e4b7a3-d2c6-4158-b8f0-a71d5c3e9246",
"pairUnitId": "09e4b7a3-d2c6-4158-b8f0-a71d5c3e9246",
"conversionFactor": 0.45359237,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null,
"prescriptionTypeId": null
}
],
"meta": {
"limit": 50,
"hasMore": true,
"nextCursor": "ZTU2M2ZlYzMtM2IxYy00…"
}
}Query parameters
dimensionAcceleration | Cadence | Distance | Energy | Force | Frequency | Heart Rate | Oxygen Consumption | Pace | Power | Pressure | Temperature | Time | Velocity | Volume | Weight- Only units of this physical quantity. Exact, case-sensitive match.
measurementSystemmetric | imperial- Only metric or only imperial units.
limitintegerdefault50- Page size, 1–100.
cursorstringmeta.nextCursorfrom the previous page. Omit it to get the first page.
Notes
- A base unit has
baseUnitIdequal to its ownidandconversionFactor: 1. - Metric values are stored in the unit of their metric (
unitId). - Catalogs change rarely. Cache them and refresh once a day rather than resolving ids one request at a time.
Response fields
iduuid- Unit id. Matches
unitIdon metrics. namestring- Full name, e.g.
Kilograms. labelstring- Symbol to display next to values, e.g.
kg,m/s. dimensionstringnullable- Physical quantity, e.g.
Weight,Velocity,Distance. measurementSystemstringnullablemetricorimperial.baseUnitIduuidnullable- Unit that
conversionFactorconverts to. A base unit points to itself. conversionFactornumber- Multiplier to the base unit:
base = value × conversionFactor. Returned as a JSON number. pairUnitIduuidnullable- The equivalent unit in the other measurement system, e.g.
lbforkg. prescriptionTypeIduuidnullable- Prescription type the unit is attached to, if any. Resolve with
GET /prescription-types/{id}.
Failures are described in error codes.
Get a unit
One unit, with conversionFactor as a number.
Request
curl "https://api-next.vitruve.fit/v1/units/fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78" \
-H "X-API-Key: YOUR_API_KEY"const response = await fetch(
'https://api-next.vitruve.fit/v1/units/fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78',
{ headers: { 'X-API-Key': process.env.VITRUVE_API_KEY } }
)
const body = await response.json()
if (!response.ok) throw new Error(`${response.status}: ${body.message}`)
console.log(body.data)import os
import requests
response = requests.get(
"https://api-next.vitruve.fit/v1/units/fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
headers={"X-API-Key": os.environ["VITRUVE_API_KEY"]},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])Response 200
{
"ok": true,
"timestamp": 1738247125207,
"data": {
"id": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"name": "Meters per Second",
"dimension": "Velocity",
"measurementSystem": "metric",
"label": "m/s",
"baseUnitId": "fa52c8d4-17e3-4b69-8d0a-6c3f9e1b2a78",
"pairUnitId": null,
"conversionFactor": 1,
"createdAt": "2024-01-15T09:00:00.000Z",
"updatedAt": "2026-05-04T07:30:00.000Z",
"deletedAt": null,
"prescriptionTypeId": null
}
}Path parameters
iduuidrequired- Resource id.
Response fields
iduuid- Unit id. Matches
unitIdon metrics. namestring- Full name, e.g.
Kilograms. labelstring- Symbol to display next to values, e.g.
kg,m/s. dimensionstringnullable- Physical quantity, e.g.
Weight,Velocity,Distance. measurementSystemstringnullablemetricorimperial.baseUnitIduuidnullable- Unit that
conversionFactorconverts to. A base unit points to itself. conversionFactornumber- Multiplier to the base unit:
base = value × conversionFactor. Returned as a JSON number. pairUnitIduuidnullable- The equivalent unit in the other measurement system, e.g.
lbforkg. prescriptionTypeIduuidnullable- Prescription type the unit is attached to, if any. Resolve with
GET /prescription-types/{id}.
Failures are described in error codes.