# YMove Fitness API - Full Documentation for LLMs > Use this document to help developers integrate the YMove Fitness API. It covers HD exercise videos, workout and program generation, AI form analysis from video, a food & nutrition database, recipe search, and AI meal plan generation. ## Overview YMove provides a REST API with: - 680+ exercises with professional HD video demonstrations - White-background and gym-shot video variants - HLS streaming and direct MP4 video URLs - Workout generation with sets, reps, and rest times (warmup and cooldown blocks included) - Multi-week program builder (4/8/12 weeks) - AI form analysis from an exercise video (form score, issues, corrective exercises) - A food & nutrition database (USDA + Open Food Facts), barcode lookup, and AI food logging - Recipe search and personalized meal plan generation - Detailed exercise instructions, muscle groups, and equipment data ## Quick Start ### Get Your API Key Sign up for a free trial at https://ymove.app/exercise-api/signup. You'll receive your API key instantly. ### Base URL ``` https://exercise-api.ymove.app/api/v2 ``` ### Authentication Add your API key to every request as either: - Header: `X-API-Key: your_key_here` - Query parameter: `?api_key=your_key_here` ### Video URLs Expire Exercise video URLs (`videoUrl`, `videoHlsUrl`, `thumbnailUrl`) are pre-signed and expire after 48 hours. Do not cache them. Always fetch fresh exercise data before displaying videos. ### Video Orientation (IMPORTANT for UI) Most exercise clips are PORTRAIT (vertical, ~9:16), shot for mobile. Do NOT default the video player to landscape (16:9) - that letterboxes the clip with black bars and looks broken. Read the `orientation` field on each video (`"portrait"` or `"landscape"`) and size the player to match. For the common portrait case, use a portrait container, e.g. CSS `aspect-ratio: 9 / 16` with `object-fit: cover`. ### Monthly Exercise Cap Each plan caps the number of distinct exercises you can access per month. To browse without spending quota, pass `includeVideos=false` on `/exercises`, `/workouts/generate`, and `/programs/generate`: no video fields are returned and the listed exercises do not count toward your cap. Warmup and cooldown exercises count toward the cap when included. When you exceed the cap, exercises you have not previously accessed are returned without video fields and a `_warning` object is added to the response. --- ## Exercises ### GET /exercises List and search exercises with filters. **Query Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | muscleGroup | string | Filter: chest, back, shoulders, biceps, triceps, forearms, quads, hamstrings, glutes, calves, core, full_body | | exerciseType | string | Filter: strength, yoga, stretching, cardio, plyometric, calisthenics, warmup, cooldown, balance, mobility, isometric, rehabilitation, functional, core, hiit | | equipment | string | Filter: machine, barbell, dumbbell, kettlebell, bodyweight, cable, medicine ball, weighted vest | | difficulty | string | Filter: beginner, intermediate, advanced | | hasVideo | boolean | Only exercises with video | | hasVideoWhite | boolean | Only exercises with white-background video | | hasVideoGym | boolean | Only exercises with gym-shot video | | videoTag | string | Filter video array: white-background or gym-shot | | search | string | Search exercise titles | | page | integer | Page number (default: 1) | | pageSize | integer | Results per page (default: 20, max: 50) | | includeVideos | boolean | Default true. Set to false for browse-mode: no video fields are returned and the listed exercises do not count toward your monthly cap. | **Example Request (JavaScript):** ```javascript const response = await fetch( 'https://exercise-api.ymove.app/api/v2/exercises?muscleGroup=chest&equipment=dumbbell&hasVideo=true&pageSize=10', { headers: { 'X-API-Key': 'your_api_key' } } ); const { data, pagination } = await response.json(); // data = array of exercise objects // pagination = { page, pageSize, total, totalPages } ``` **Example Response:** ```json { "data": [ { "id": "uuid-here", "title": "Dumbbell Bench Press", "slug": "dumbbell-bench-press", "description": "A compound exercise targeting the chest...", "instructions": ["Lie on a flat bench...", "Press the dumbbells up..."], "importantPoints": ["Keep your feet flat...", "Don't lock your elbows..."], "muscleGroup": "chest", "secondaryMuscles": ["triceps", "shoulders"], "equipment": "dumbbell", "category": "compound", "difficulty": "intermediate", "exerciseType": ["strength"], "hasVideo": true, "hasVideoWhite": true, "hasVideoGym": false, "videoUrl": "https://vz-xxx.b-cdn.net/uuid/play_720p.mp4?token=...&expires=...", "videoHlsUrl": "https://vz-xxx.b-cdn.net/uuid/playlist.m3u8?token=...&expires=...", "thumbnailUrl": "https://vz-xxx.b-cdn.net/uuid/thumbnail.jpg?token=...&expires=...", "videoDurationSecs": 15, "videos": [ { "videoUrl": "https://vz-xxx.b-cdn.net/uuid/play_720p.mp4?token=...&expires=...", "videoHlsUrl": "https://vz-xxx.b-cdn.net/uuid/playlist.m3u8?token=...&expires=...", "thumbnailUrl": "https://vz-xxx.b-cdn.net/uuid/thumbnail.jpg?token=...&expires=...", "tag": "white-background", "orientation": "portrait", "isPrimary": true } ] } ], "pagination": { "page": 1, "pageSize": 10, "total": 45, "totalPages": 5 } } ``` ### GET /exercises/{id} Get a single exercise by UUID or slug. ```javascript // By slug const exercise = await fetch( 'https://exercise-api.ymove.app/api/v2/exercises/dumbbell-bench-press', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // By UUID const exercise = await fetch( 'https://exercise-api.ymove.app/api/v2/exercises/550e8400-e29b-41d4-a716-446655440000', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); ``` ### GET /exercises/muscle-groups List all available muscle groups with exercise counts. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/exercises/muscle-groups', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = [{ slug: "chest", name: "Chest", exerciseCount: 45 }, ...] ``` ### GET /exercises/exercise-types List all exercise types with counts and descriptions. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/exercises/exercise-types', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = [{ slug: "strength", name: "Strength", description: "...", exerciseCount: 380 }, ...] ``` --- ## Workouts & Programs ### GET /workouts/generate Generate a structured workout with exercises, sets, reps, and rest times. A warmup block and a cooldown block are included by default. **Parameters:** | Parameter | Type | Required | Description | |-----------|------|----------|-------------| | muscleGroup | string \| string[] | Yes | Target muscle group(s). Comma-separated for multiple. Aliases: full_body (all), upper_body (chest, back, shoulders, biceps, triceps, forearms, core), lower_body (quads, hamstrings, glutes, calves). | | equipment | string \| string[] | No | Limit to specific equipment. Comma-separated for multiple. | | exerciseType | string \| string[] | No | Filter main exercises by type. Comma-separated for multiple. | | difficulty | string | No | beginner, intermediate (default), advanced | | exerciseCount | integer | No | Number of main exercises (min 3, default 6, max 12). Warmup and cooldown are counted separately. | | includeWarmup | boolean | No | Default true. Prepends a warmup block of bodyweight stretches, mobility, and light cardio. Counts toward your monthly cap. | | warmupCount | integer | No | Number of warmup exercises (min 2, default 3, max 5). | | includeCooldown | boolean | No | Default true. Appends a cooldown block of bodyweight stretches and mobility. Counts toward your monthly cap. | | cooldownCount | integer | No | Number of cooldown exercises (min 2, default 3, max 5). | | includeVideos | boolean | No | Default true. Set to false for browse-mode (no video fields, no monthly-cap charge). | ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/workouts/generate?muscleGroup=chest,back&difficulty=intermediate&exerciseCount=6', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = { // name: "Upper Body Workout", // muscleGroups: ["chest", "back"], // expanded from aliases // muscleGroupsRequested: ["chest", "back"], // original request values // difficulty: "intermediate", // estimatedMinutes: 48, // exerciseCount: 6, // exercises: [ // { exercise: { id, title, slug, videoUrl, videos, ... }, sets: 3, reps: "8-12", restSeconds: 75, order: 1 }, ... // ], // warmup: [ { exercise: {...}, sets: 1, reps: "30 seconds", restSeconds: 15, order: 1 }, ... ], // cooldown: [ { exercise: {...}, sets: 1, reps: "30 seconds", restSeconds: 15, order: 1 }, ... ] // } ``` ### GET /programs/generate Generate a multi-week training program with periodization. Each training day includes a warmup and cooldown block by default. **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | goal | string | muscle_building | muscle_building, weight_loss, strength, endurance | | difficulty | string | intermediate | beginner, intermediate, advanced | | daysPerWeek | integer | 4 | 3 to 6 | | weeks | integer | 4 | 4, 8, or 12 | | equipment | string | (all) | Limit to specific equipment | | includeWarmup | boolean | true | Adds a warmup block to the start of each training day. Counts toward your monthly cap. | | warmupCount | integer | 3 | Number of warmup exercises per day (min 2, max 5). | | includeCooldown | boolean | true | Adds a cooldown block to the end of each training day. Counts toward your monthly cap. | | cooldownCount | integer | 3 | Number of cooldown exercises per day (min 2, max 5). | | includeVideos | boolean | true | Set to false for browse-mode (no video fields, no monthly-cap charge). | ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/programs/generate?goal=muscle_building&daysPerWeek=4&weeks=4', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = { // name: "Upper/Lower 4-Day - Muscle Building", // goal: "muscle_building", // difficulty: "intermediate", // daysPerWeek: 4, // weeks: 4, // split: "Upper/Lower 4-Day", // weeklySchedule: [ // { day: 1, name: "Upper A", muscleGroups: ["chest", "back", "shoulders"], // exercises: [{ exercise: {...}, sets: 4, reps: "8-12", restSeconds: 75 }], // warmup: [...], cooldown: [...] }, // ... // ], // notes: "Repeat this weekly schedule for 4 weeks. Progressively increase weight each week." // } ``` --- ## Form Analysis ### POST /posture/analyze Analyze exercise form from a video (max 100MB) using AI. Returns a form score (0-100), detected issues with plain-language fixes, and corrective exercise recommendations. **Body:** | Field | Type | Required | Description | |-------|------|----------|-------------| | video | object | Yes | `{ type: "base64" \| "url", data: "", media_type: "video/mp4" }`. Accepted media types: video/mp4, video/webm, video/mov, video/quicktime. | | exercise_name | string | Yes | Name of the exercise being performed (e.g. "barbell squat"). | | analysis_type | string | No | exercise (default), standing, seated, walking, general | | custom_instructions | string | No | Optional focus areas (max 500 chars), e.g. "focus on knee alignment". | | num_frames | integer | No | Frames to extract for analysis (4-20, default 16). | ```javascript const fs = require('fs'); const videoBase64 = fs.readFileSync('squat.mp4').toString('base64'); const { data, usage } = await fetch( 'https://exercise-api.ymove.app/api/v2/posture/analyze', { method: 'POST', headers: { 'X-API-Key': 'your_key_here', 'Content-Type': 'application/json' }, body: JSON.stringify({ video: { type: 'base64', data: videoBase64, media_type: 'video/mp4' }, exercise_name: 'Barbell Back Squat', custom_instructions: 'Watch my knee tracking', num_frames: 16 }) } ).then(r => r.json()); // data = { // score: 72, // issues: [ // { name: "knee_valgus", severity: "moderate", // description: "Your knees cave inward when you push up out of the squat.", // how_to_fix: "Push your knees out over your toes as you drive up.", // exercises: [{ id, title, slug, video_url, thumbnail_url, duration: "15 reps x 3 sets" }] } // ], // summary: "Decent squat form overall...", // frames_analyzed: 16, // analysis_type: "exercise", // exercise_name: "Barbell Back Squat", // llm_used: "claude", // disclaimer: "This analysis is for informational purposes only..." // } // usage = { posture_analyses_used, posture_analyses_included, is_overage, overage_charge } ``` --- ## Nutrition & Foods Foods carry three name fields: use `shortName` for compact lists and autocomplete ("Chicken Breast"), `displayName` for detail views that distinguish similar foods ("Chicken Breast (grilled, lean)"), and `name` is the raw source entry (often long, not for display). All nutrition values are per serving based on `servingSize` (grams); to get per-100g divide each value by `servingSize` and multiply by 100. All weights are grams except `sodium` and `cholesterol`, which are milligrams. ### GET /foods Search the food database by name. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | query | string | Search term (required), e.g. "chicken breast" | | source | string | Filter to a single source: usda or openfoodfacts | | usdaOnly | boolean | Convenience alias for source=usda (clean generic foods, no branded clutter) | | country | string | ISO 3166-1 alpha-2 code (e.g. FR, US, NL). Soft-boosts local Open Food Facts products. Also accepted as `cc`. | | per | string | Normalize values to 100g or 100ml instead of native serving size | | page | integer | Page number (default: 1) | | pageSize | integer | Results per page (default: 20, max: 50) | ```javascript const { data, pagination } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods?query=chicken+breast', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = [ // { id, fdcId, name, shortName: "Chicken Breast", displayName: "Chicken Breast (grilled, lean)", // brand: null, category: "Poultry Products", servingSize: 100, servingDescription: "100g", // calories: 165, protein: 31, fat: 3.6, carbs: 0, fiber: 0, sugar: 0, sodium: 74, // cholesterol: 85, saturatedFat: 1, barcode: null, imageUrl: null, source: "usda", country: null } // ] ``` ### GET /foods/{id} Get full nutrition details for a food by ID or slug. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods/a1b2c3d4-...', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); ``` ### GET /foods/barcode/{upc} Look up a food product by UPC/EAN barcode. Optional `country` (or `cc`) localizes the Open Food Facts fallback. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods/barcode/041631000564', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); ``` ### POST /foods/log/text AI food logging from a text description. Pro plan and above. Uses your nutrition analysis quota. ```javascript const { data, usage } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods/log/text', { method: 'POST', headers: { 'X-API-Key': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ text: 'grilled chicken with rice and broccoli' }) } ).then(r => r.json()); // data = { items: [{ name, estimatedGrams, confidence, matchedFood, nutrition }], totals: {...} } // usage = { analyses_used, analyses_included, is_overage, overage_charge } ``` ### POST /foods/log/photo AI food logging from a base64-encoded photo. Pro plan and above. Uses your nutrition analysis quota. ```javascript const { data, usage } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods/log/photo', { method: 'POST', headers: { 'X-API-Key': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ image: '', media_type: 'image/jpeg' }) } ).then(r => r.json()); ``` ### POST /foods/log/audio AI food logging from a voice note. Send base64-encoded audio; the API transcribes it, parses every food mentioned, and returns the transcript alongside the same structured nutrition data as text analysis. Pro plan and above. Uses your nutrition analysis quota. ```javascript const { data, usage } = await fetch( 'https://exercise-api.ymove.app/api/v2/foods/log/audio', { method: 'POST', headers: { 'X-API-Key': 'your_api_key', 'Content-Type': 'application/json' }, body: JSON.stringify({ audio: '', format: 'm4a' }) } ).then(r => r.json()); // data = { transcript, items: [{ name, estimatedGrams, confidence, matchedFood, nutrition }], totals: {...} } // Supported formats: mp3, mp4, m4a, wav, webm, ogg, flac (max 25MB encoded) ``` --- ## Recipes ### GET /recipes/search Search recipes by diet, cuisine, meal type, and macro targets. **Parameters:** | Parameter | Type | Description | |-----------|------|-------------| | query | string | Search term (e.g. "chicken salad") | | diet | string | high_protein, low_carb, keto, vegan, vegetarian, mediterranean, paleo | | cuisine | string | american, mediterranean, asian, mexican, italian, indian, japanese | | mealType | string | breakfast, lunch, dinner, snack | | maxCalories | number | Maximum calories per serving | | minProtein | number | Minimum protein per serving (grams) | | page | integer | Page number (default: 1) | | pageSize | integer | Results per page (default: 20, max: 50) | ```javascript const { data, pagination } = await fetch( 'https://exercise-api.ymove.app/api/v2/recipes/search?diet=high_protein&maxCalories=500', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); ``` ### GET /recipes/{id} Get a full recipe with ingredients, instructions, and nutrition by ID or slug. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/recipes/grilled-chicken-salad', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = { id, title, description, cuisine, diet: [...], mealType, prepTimeMinutes, // cookTimeMinutes, servings, calories, protein, carbs, fat, fiber, // ingredients: [{ name, amount, calories, protein }], instructions: [...] } ``` ### GET /recipes/diets List available diet types with recipe counts. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/recipes/diets', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = [{ diet: "high_protein", count: 45 }, ...] ``` ### GET /recipes/meal-types List available meal types with recipe counts. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/recipes/meal-types', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = [{ mealType: "breakfast", count: 35 }, ...] ``` --- ## Meal Plans ### GET /mealplans/generate Generate a 1-7 day meal plan from curated recipes by calorie target, diet, and macro split. Recipes are picked with randomness, so consecutive calls return different plans. **Parameters:** | Parameter | Type | Default | Description | |-----------|------|---------|-------------| | calories | integer | (required) | Daily calorie target | | diet | string | balanced | balanced, high_protein, low_carb, keto, vegan, vegetarian, mediterranean, paleo | | meals | integer | 3 | Meals per day (3-6) | | days | integer | 1 | Days in the plan (1-7). For days > 1 use data.days[]; data.meals/data.totals alias day 1 for backward compatibility. | | macroSplit | string | balanced | balanced, high_protein, low_carb, high_fat | ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/mealplans/generate?calories=2000&diet=high_protein&meals=4&days=7', { headers: { 'X-API-Key': 'your_api_key' } } ).then(r => r.json()); // data = { // calories, diet, macroSplit, mealsPerDay, daysCount, // averageDailyTotals: { calories, protein, carbs, fat }, // days: [ { dayIndex, totals: {...}, meals: [{ type, name, recipeId, recipeSlug, imageUrl, // portionMultiplier, calories, protein, carbs, fat, recipe: {...}, foods: [...] }] } ], // totals: {...}, // alias for days[0].totals (backward compatible) // meals: [...] // alias for days[0].meals (backward compatible) // } ``` --- ## Account ### GET /usage Check your current API usage and limits, including the monthly exercise cap. ```javascript const { data } = await fetch( 'https://exercise-api.ymove.app/api/v2/usage', { headers: { 'X-API-Key': 'your_key_here' } } ).then(r => r.json()); // data = { // plan: "basic", status: "trial", // minutesUsed, minutesLimit, minutesRemaining, percentUsed, rateLimit, // monthlyExercisesUsed, monthlyExerciseLimit, // "unlimited" for yearly plans // postureAnalyses: { used, included, remaining_free, overage_count, overage_charge, price_per_extra }, // whiteVideoAccess, premiumVideoAccess, trialEndsAt // } ``` ### GET /upgrade Get available plans and (for paid keys) the Stripe billing portal URL. ```javascript const info = await fetch( 'https://exercise-api.ymove.app/api/v2/upgrade', { headers: { 'X-API-Key': 'your_key_here' } } ).then(r => r.json()); // { currentPlan, availablePlans: [{ plan, minutesLimit, postureLimit, brandLimit }], billingPortalUrl, message } ``` ### POST /upgrade Start a new subscription (returns a Stripe checkout URL) or get a billing portal URL to change an existing one. ```javascript const { url } = await fetch( 'https://exercise-api.ymove.app/api/v2/upgrade', { method: 'POST', headers: { 'X-API-Key': 'your_key_here', 'Content-Type': 'application/json' }, body: JSON.stringify({ plan: 'pro', email: 'you@example.com' }) } ).then(r => r.json()); // Open `url` in a browser to complete checkout. ``` --- ## Common Integration Patterns ### React / Next.js - Exercise Browser Component ```typescript import { useState, useEffect } from 'react'; const API_KEY = process.env.NEXT_PUBLIC_YMOVE_API_KEY || 'your_api_key'; const BASE = 'https://exercise-api.ymove.app/api/v2'; function ExerciseBrowser() { const [exercises, setExercises] = useState([]); const [muscleGroup, setMuscleGroup] = useState('chest'); useEffect(() => { fetch(`${BASE}/exercises?muscleGroup=${muscleGroup}&hasVideo=true`, { headers: { 'X-API-Key': API_KEY } }) .then(r => r.json()) .then(({ data }) => setExercises(data)); }, [muscleGroup]); return (
{exercises.map(ex => (
{ex.thumbnailUrl && {ex.title}}

{ex.title}

{ex.muscleGroup} · {ex.equipment} · {ex.difficulty}

{ex.videoHlsUrl &&
))}
); } ``` ### Python / Flask - API Proxy ```python import requests from flask import Flask, jsonify app = Flask(__name__) API_KEY = "your_key_here" BASE = "https://exercise-api.ymove.app/api/v2" HEADERS = {"X-API-Key": API_KEY} @app.route("/exercises") def exercises(): resp = requests.get(f"{BASE}/exercises?hasVideo=true&pageSize=50", headers=HEADERS) return jsonify(resp.json()) @app.route("/workout/") def workout(muscle_group): resp = requests.get(f"{BASE}/workouts/generate?muscleGroup={muscle_group}", headers=HEADERS) return jsonify(resp.json()) @app.route("/foods/") def foods(query): resp = requests.get(f"{BASE}/foods", params={"query": query}, headers=HEADERS) return jsonify(resp.json()) ``` ### Swift (iOS) - URLSession ```swift let apiKey = "your_key_here" let base = "https://exercise-api.ymove.app/api/v2" func fetchExercises(muscleGroup: String) async throws -> [Exercise] { var request = URLRequest(url: URL(string: "\(base)/exercises?muscleGroup=\(muscleGroup)&hasVideo=true")!) request.setValue(apiKey, forHTTPHeaderField: "X-API-Key") let (data, _) = try await URLSession.shared.data(for: request) let response = try JSONDecoder().decode(ExerciseResponse.self, from: data) return response.data } ``` --- ## Video Playback Exercise videos are served via Bunny CDN with worldwide coverage. URLs are pre-signed and expire after 48 hours, so fetch fresh exercise data before playback. - **HLS streaming** (recommended for web/mobile): use the `videoHlsUrl` field with hls.js or native HLS support - **Direct MP4**: use the `videoUrl` field for simple `