Content API

The KinesteX Content API provides access to workout plans, individual workouts, and exercises. Use it to build custom content browsers, create personalized workout recommendations, or integrate KinesteX content into your app.


SDK vs REST API:

  • Swift, Kotlin, Flutter: Use the built-in SDK convenience methods (recommended)
  • React Native, React TypeScript, HTML/JS: Use direct REST API calls

Base URL: `https://admin.kinestex.com/api/v1/`


Available Endpoints:

EndpointDescription
/workoutsFetch workout content
/plansFetch workout plan content
/exercisesFetch exercise content

Headers (REST API only):

HeaderDescription
x-api-keyYour API key for authentication
x-company-nameThe name of your company

Getting Started

Before using the Content API, ensure your SDK is properly initialized. The API methods are only available after initialization.


For SDK platforms (Swift, Kotlin, Flutter): Initialize the SDK with your credentials

For REST platforms (React Native, React TypeScript, HTML/JS): Set up your request headers

Initialize SDK / Setup Headers
1import KinesteXAIKit
2
3// Initialize KinesteXAIKit with your credentials
4let kinestex = KinesteXAIKit(
5    apiKey: "YOUR_API_KEY",
6    companyName: "YOUR_COMPANY",
7    userId: "user_123"
8)
9
10// Now you can use the Content API methods:
11// - kinestex.fetchWorkouts()
12// - kinestex.fetchPlans()
13// - kinestex.fetchExercises()
14// - kinestex.fetchWorkout(id:)
15// - kinestex.fetchPlan(id:)
16// - kinestex.fetchExercise(id:)
17// - kinestex.fetchContent(contentType:, ...)

Available SDK Methods

KinesteXAIKit provides convenient methods that handle all the complexity of API calls for you.

Convenience Methods (Recommended)
1// Fetch lists with optional filters
2func fetchWorkouts(category: String? = nil, bodyParts: [BodyPart]? = nil, limit: Int? = 10, lastDocId: String? = nil, lang: String = "en") async -> Result<WorkoutsResponse, Error>
3
4func fetchExercises(bodyParts: [BodyPart]? = nil, limit: Int? = 10, lastDocId: String? = nil, lang: String = "en") async -> Result<ExercisesResponse, Error>
5
6func fetchPlans(category: String? = nil, limit: Int? = 10, lastDocId: String? = nil, lang: String = "en") async -> Result<PlansResponse, Error>
7
8// Fetch single items by ID
9func fetchWorkout(id: String, lang: String = "en") async -> Result<WorkoutModel, Error>
10func fetchExercise(id: String, lang: String = "en") async -> Result<ExerciseModel, Error>
11func fetchPlan(id: String, lang: String = "en") async -> Result<PlanModel, Error>
Advanced Method (Full Control)
1// Use fetchContent for advanced filtering or when you need the raw result type
2func fetchContent(
3    contentType: ContentType,  // .workout, .plan, .exercise
4    id: String? = nil,
5    title: String? = nil,
6    lang: String = "en",
7    category: String? = nil,
8    bodyParts: [BodyPart]? = nil,
9    lastDocId: String? = nil,
10    limit: Int? = nil
11) async -> APIContentResult

Fetching Content Lists

Fetch lists of workouts, plans, or exercises with optional filtering.


Parameters:

ParameterTypeDescription
categoryStringFilter by category (optional)
bodyParts[BodyPart]Filter by targeted body parts (optional)
limitIntNumber of results to return (default: 10)
langStringLanguage code (default: "en")
lastDocIdStringFor pagination (optional)
Fetch Workouts
1// Fetch workouts with optional filters
2Task {
3    let result = await kinestex.fetchWorkouts(
4        category: "Fitness",  // or "Rehabilitation"
5        limit: 10
6    )
7
8    switch result {
9    case .success(let response):
10        let workouts = response.workouts
11        print("Fetched \(workouts.count) workouts")
12
13        for workout in workouts {
14            print("- \(workout.title): \(workout.totalMinutes ?? 0) mins")
15        }
16
17        // Store lastDocId for pagination
18        let nextPageId = response.lastDocId
19
20    case .failure(let error):
21        print("Error: \(error.localizedDescription)")
22    }
23}
Fetch Plans
1// Fetch workout plans
2Task {
3    let result = await kinestex.fetchPlans(
4        category: "Strength",  // Rehabilitation, Weight Management, Cardio, Strength
5        limit: 5
6    )
7
8    switch result {
9    case .success(let response):
10        let plans = response.plans
11        print("Fetched \(plans.count) plans")
12
13    case .failure(let error):
14        print("Error: \(error.localizedDescription)")
15    }
16}
Fetch Exercises
1// Fetch exercises filtered by body parts
2Task {
3    let result = await kinestex.fetchExercises(
4        bodyParts: [.abs, .glutes],
5        limit: 10
6    )
7
8    switch result {
9    case .success(let response):
10        let exercises = response.exercises
11        print("Fetched \(exercises.count) exercises")
12
13        for exercise in exercises {
14            print("- \(exercise.title): \(exercise.bodyParts.joined(separator: ", "))")
15        }
16
17    case .failure(let error):
18        print("Error: \(error.localizedDescription)")
19    }
20}

Fetching Single Items

Fetch a specific workout, plan, or exercise by ID or title.


By ID: Use the unique document ID for exact match

By Title: Use the content title (case-insensitive, returns first match)

Fetch by ID
1// Fetch a specific workout by ID
2Task {
3    let result = await kinestex.fetchWorkout(id: "9zE1kzOzpU5d5dAJrPOY")
4
5    switch result {
6    case .success(let workout):
7        print("Workout: \(workout.title)")
8        print("Duration: \(workout.totalMinutes ?? 0) minutes")
9        print("Calories: \(workout.totalCalories ?? 0)")
10        print("Exercises: \(workout.sequence.count)")
11
12    case .failure(let error):
13        print("Error: \(error.localizedDescription)")
14    }
15}
16
17// Fetch a specific exercise by ID
18Task {
19    let result = await kinestex.fetchExercise(id: "jz73VFlUyZ9nyd64OjRb")
20
21    switch result {
22    case .success(let exercise):
23        print("Exercise: \(exercise.title)")
24        print("Model ID: \(exercise.modelId)")
25
26    case .failure(let error):
27        print("Error: \(error.localizedDescription)")
28    }
29}
30
31// Fetch a specific plan by ID
32Task {
33    let result = await kinestex.fetchPlan(id: "22B3qRU2r75hVXHgGiGx")
34
35    switch result {
36    case .success(let plan):
37        print("Plan: \(plan.title)")
38
39    case .failure(let error):
40        print("Error: \(error.localizedDescription)")
41    }
42}
Fetch by Title
1// Fetch content by title (returns first match)
2Task {
3    let result = await kinestex.fetchContent(
4        contentType: .workout,
5        title: "Fitness Lite"
6    )
7
8    switch result {
9    case .workout(let workout):
10        print("Found workout: \(workout.title)")
11
12    case .error(let message):
13        print("Error: \(message)")
14
15    default:
16        print("Unexpected result type")
17    }
18}

Filtering & Parameters

Filter content by category and body parts for targeted results.


Workout Categories: Fitness, Rehabilitation


Plan Categories: Rehabilitation, Weight Management, Cardio, Strength


Body Parts (BodyPart enum):

SDK Value (Swift)SDK Value (Kotlin)SDK Value (Flutter)REST API Value
.absABSBodyPart.absAbs
.bicepsBICEPSBodyPart.bicepsBiceps
.calvesCALVESBodyPart.calvesCalves
.chestCHESTBodyPart.chestChest
.externalObliqueEXTERNAL_OBLIQUEBodyPart.externalObliqueExternal Oblique
.forearmsFOREARMSBodyPart.forearmsForearms
.glutesGLUTESBodyPart.glutesGlutes
.hamstringsHAMSTRINGSBodyPart.hamstringsHamstrings
.latsLATSBodyPart.latsLats
.lowerBackLOWER_BACKBodyPart.lowerBackLower Back
.neckNECKBodyPart.neckNeck
.quadsQUADSBodyPart.quadsQuads
.shouldersSHOULDERSBodyPart.shouldersShoulders
.trapsTRAPSBodyPart.trapsTraps
.tricepsTRICEPSBodyPart.tricepsTriceps
.fullBodyFULL_BODYBodyPart.fullBodyFull Body
Filter by Category and Body Parts
1// Combine category and body parts filters
2Task {
3    let result = await kinestex.fetchContent(
4        contentType: .workout,
5        category: "Fitness",
6        bodyParts: [.abs, .glutes, .quads],
7        limit: 10
8    )
9
10    switch result {
11    case .workouts(let response):
12        let workouts = response.workouts
13        print("Found \(workouts.count) workouts targeting abs, glutes, and quads")
14
15    case .error(let message):
16        print("Error: \(message)")
17
18    default:
19        break
20    }
21}

Pagination

For large result sets, use pagination with the `lastDocId` parameter to fetch subsequent pages.


How it works:

1. First request: Omit `lastDocId` to get the first page

2. Store the ID: Save the `lastDocId` from the response

3. Next request: Pass the saved ID as `lastDocId` to get the next page

4. Repeat: Continue until no more results are returned

Paginated Fetching
1// Fetch all workouts with pagination
2func fetchAllWorkouts() async throws -> [WorkoutModel] {
3    var allWorkouts: [WorkoutModel] = []
4    var lastDocId: String? = nil
5
6    repeat {
7        let result = await kinestex.fetchWorkouts(
8            category: "Fitness",
9            limit: 10,
10            lastDocId: lastDocId
11        )
12
13        switch result {
14        case .success(let response):
15            allWorkouts.append(contentsOf: response.workouts)
16            lastDocId = response.lastDocId
17
18            // If lastDocId is empty or nil, we've reached the end
19            if lastDocId?.isEmpty ?? true {
20                lastDocId = nil
21            }
22
23            print("Fetched page with \(response.workouts.count) workouts")
24
25        case .failure(let error):
26            throw error
27        }
28    } while lastDocId != nil
29
30    print("Total workouts fetched: \(allWorkouts.count)")
31    return allWorkouts
32}

Error Handling

Handle API errors gracefully in your application.


Response Codes:

StatusDescription
200/201Request successful
400Validation error (check parameters)
401Unauthorized (invalid API key)
404Content not found
500Internal server error
Error Handling Patterns
1// Comprehensive error handling with Swift SDK
2Task {
3    let result = await kinestex.fetchWorkouts(category: "Fitness", limit: 10)
4
5    switch result {
6    case .success(let response):
7        // Handle successful response
8        let workouts = response.workouts
9        print("Success: Fetched \(workouts.count) workouts")
10
11    case .failure(let error):
12        // Handle different error types
13        if let urlError = error as? URLError {
14            switch urlError.code {
15            case .notConnectedToInternet:
16                print("No internet connection")
17            case .timedOut:
18                print("Request timed out")
19            default:
20                print("Network error: \(urlError.localizedDescription)")
21            }
22        } else {
23            print("Error: \(error.localizedDescription)")
24        }
25    }
26}
27
28// Using fetchContent for advanced error handling
29Task {
30    let result = await kinestex.fetchContent(
31        contentType: .workout,
32        id: "invalid_id"
33    )
34
35    switch result {
36    case .workout(let workout):
37        print("Found: \(workout.title)")
38
39    case .error(let message):
40        // API returned an error message
41        print("API Error: \(message)")
42
43    case .rawData(let data, let errorMessage):
44        // Parsing failed, but raw data is available
45        print("Parse error: \(errorMessage ?? "Unknown")")
46        print("Raw data: \(data)")
47
48    default:
49        print("Unexpected result type")
50    }
51}

Data Models

Reference for the data structures returned by the Content API.


WorkoutModel:

FieldTypeDescription
idStringUnique identifier
titleStringWorkout name
categoryStringFitness or Rehabilitation
caloriesInt?Estimated calories burned
totalMinutesInt?Total duration in minutes
bodyParts[String]Targeted body parts
difficultyLevelString?Difficulty level
descriptionStringWorkout description
imgURLStringWorkout thumbnail image
sequence[ExerciseModel]List of exercises

ExerciseModel:

FieldTypeDescription
idStringUnique identifier
titleStringExercise name
bodyParts[String]Targeted body parts
videoURLStringDemo video URL
thumbnailURLStringThumbnail image URL
modelIdStringMotion tracking model ID (use in Camera Component)
descriptionStringExercise description
steps[String]Step-by-step instructions
commonMistakesStringCommon mistakes to avoid
tipsStringTips for proper form

PlanModel:

FieldTypeDescription
idStringUnique identifier
titleStringPlan name
imgURLStringPlan thumbnail image
categoryPlanModelCategoryCategory with description and levels
levels[String: PlanLevel]Dictionary of levels (1, 2, 3, etc.)
createdByStringCreator identifier

PlanLevel:

FieldTypeDescription
titleStringLevel title
descriptionStringLevel description
days[String: PlanDay]Dictionary of days

PlanDay:

FieldTypeDescription
titleStringDay title
descriptionStringDay description
workouts[WorkoutSummary]?List of workouts for this day
Working with Models
1// Accessing workout model properties
2Task {
3    let result = await kinestex.fetchWorkout(id: "9zE1kzOzpU5d5dAJrPOY")
4
5    switch result {
6    case .success(let workout):
7        // Basic properties
8        print("Title: \(workout.title)")
9        print("Category: \(workout.category ?? "N/A")")
10        print("Duration: \(workout.totalMinutes ?? 0) minutes")
11        print("Calories: \(workout.totalCalories ?? 0)")
12        print("Difficulty: \(workout.difficultyLevel ?? "N/A")")
13
14        // Body parts
15        print("Targets: \(workout.bodyParts.joined(separator: ", "))")
16
17        // Exercise sequence
18        print("\nExercises (\(workout.sequence.count)):")
19        for (index, exercise) in workout.sequence.enumerated() {
20            print("\(index + 1). \(exercise.title)")
21            print("   Model ID: \(exercise.modelId)")  // Use for Camera Component
22            print("   Reps: \(exercise.workoutReps ?? exercise.averageReps ?? 0)")
23        }
24
25        // Access raw JSON if needed
26        if let rawJSON = workout.rawJSON {
27            print("\nRaw JSON available: \(rawJSON.keys.count) keys")
28        }
29
30    case .failure(let error):
31        print("Error: \(error.localizedDescription)")
32    }
33}
34
35// Working with plan structure
36Task {
37    let result = await kinestex.fetchPlan(id: "22B3qRU2r75hVXHgGiGx")
38
39    switch result {
40    case .success(let plan):
41        print("Plan: \(plan.title)")
42        print("Category: \(plan.category.description)")
43
44        // Iterate through levels
45        for (levelKey, level) in plan.levels {
46            print("\nLevel \(levelKey): \(level.title)")
47
48            // Iterate through days
49            for (dayKey, day) in level.days {
50                print("  Day \(dayKey): \(day.title)")
51
52                // List workouts for this day
53                if let workouts = day.workouts {
54                    for workout in workouts {
55                        print("    - \(workout.title) (\(workout.totalMinutes) min)")
56                    }
57                }
58            }
59        }
60
61    case .failure(let error):
62        print("Error: \(error.localizedDescription)")
63    }
64}

Need Help?

Our team is ready to assist with your integration.

Contact Support
KinesteX

Personal AI Fitness Trainer & Motion Analysis SDK for Health & Fitness Apps

Contacts

hello@kinestex.com

Supported by

AMKM Investments supporting KinesteXin5 Tech
© 2024. All rights reserved. KinesteX
KinesteX TwitterKinesteX Inst