---
title: "Generate a multi-week training program"
method: GET
path: "/programs/generate"
tags: ["Programs"]
---

# Generate a multi-week training program

`GET /programs/generate`

## Request

- Base URL: `https://exercise-api.ymove.app/api/v2`
- URL: `https://exercise-api.ymove.app/api/v2/programs/generate`
- Auth: one of:
  - API key in header `X-API-Key`
  - API key in query parameter `api_key`

## Query parameters

- `goal` 'muscle_building' | 'weight_loss' | 'strength' | 'endurance'
- `difficulty` 'beginner' | 'intermediate' | 'advanced'
- `daysPerWeek` integer
- `weeks` 4 | 8 | 12
- `equipment` 'bodyweight' | 'dumbbell' | 'mat' | 'machine' | 'barbell' | 'cable' | 'kettlebell' | 'chair' | 'plate' | 'bosu' | 'band' | 'rings' | 'foam-roller' | 'box' | 'bench' | 'weighted-vest' | 'stability-ball' | 'suspension-trainer' | 'dip-bar' | 'mini-band' | 'medicine-ball' | 'battle-rope' | 'push-up-handles' | 'smith-machine' | 'wall' | 'trap-bar' | 'cone' | 'landmine' | 'step' | 'ab-wheel' | 'towel' | 'foam roller' | 'weighted vest' | 'stability ball' | 'suspension trainer' | 'dip bar' | 'mini band' | 'medicine ball' | 'battle rope' | 'push-up handles' | 'smith machine' | 'trap bar' | 'ab wheel' | 'free weights' | 'ez bar' | 'swiss ball' | 'exercise ball' | 'gym ball' | 'resistance band' | 'resistance bands' | 'bosu ball' | 'balance trainer' | 'pull up bar' | 'parallel bars' | 'gymnastic rings' | 'yoga mat' | 'med ball' | 'slam ball' | 'weight plate' | 'bumper plate' | 'hex bar' | 'aerobic step' | 'step platform' | 'no equipment'
- `includeWarmup` boolean
- `warmupCount` integer
- `includeCooldown` boolean
- `cooldownCount` integer
- `includeVideos` boolean

## Response `200`

Generated training program. When the monthly exercise cap is exceeded, exercises not previously accessed will have video properties removed entirely. A `_warning` object is included with details.

- object
  - `data` Program
    - `name` string
    - `goal` string
    - `difficulty` string
    - `daysPerWeek` integer
    - `weeks` integer
    - `split` string
    - `weeklySchedule` ProgramDay[]
      - `day` integer
      - `name` string
      - `muscleGroups` string[]
      - `exercises` WorkoutExercise[]
        - `exercise` Exercise
          - `id` string, uuid, required
          - `title` string, required
          - `slug` string, required
          - `description` string, nullable
          - `instructions` string[], nullable
          - `importantPoints` string[], nullable
          - `muscleGroup` string, required
          - `secondaryMuscles` string[], nullable
          - `muscleGroups` string[], nullable: Every muscle this exercise works, primary and supporting, at anatomical detail (e.g. ["back","lats","rhomboids","traps","erector_spinae"]). muscleGroup is the single coarse value the filters use; this is the detail behind it, for body maps and analysis. Populated for the whole catalogue.
          - `equipment` string, required
          - `category` string, nullable
          - `difficulty` string, nullable
          - `videoUrl` string, uri, nullable: Pre-signed CDN URL for primary video. Expires after 48 hours.
          - `videoHlsUrl` string, uri, nullable: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
          - `thumbnailUrl` string, uri, nullable: Default thumbnail (up to 600x800, 3:4) for the primary video, served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default. Returned in browse mode too, where the video URLs are not.
          - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
            - `default` string, uri, required: Up to 600x800 (3:4). The one to reach for, and what thumbnailUrl points at. A mild portrait holds a standing lift and a lying floor exercise without cramping either.
            - `square` string, uri, required: Up to 600x600 (1:1). List rows, grid tiles, anywhere the layout is already square.
            - `portrait` string, uri, required: Up to 720x1280 (9:16). Matches the video itself, for full-bleed mobile and story-style screens.
            - `landscape` string, uri, required: Up to 1280x720 (16:9). Wide cards, hero strips, and the poster frame of a video player. Tall poses are cropped to the upper body to fit the shape. Sizes are ceilings: the images are never upscaled, so a crop is only as large as the source clip allows, and older clips cap around 720px wide.
            - `original` string, uri, required: The whole frame, uncropped, as shot (portrait, ~9:16). Use it when you want to crop it yourself.
          - `videoDurationSecs` integer, nullable
          - `exerciseType` string[], nullable
          - `hasVideo` boolean, required
          - `hasVideoWhite` boolean, required
          - `hasVideoGym` boolean, required
          - `videos` Video[], required
            - `id` string, uuid, required: Stable unique ID of this video. Use it to tell apart the videos of one exercise, or to store a reference to one specific clip. It does not change when the signed URLs do.
            - `videoUrl` string, uri: Pre-signed CDN URL for MP4 video. Expires after 48 hours.
            - `videoHlsUrl` string, uri: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
            - `thumbnailUrl` string, uri: The default thumbnail (up to 600x800, 3:4), served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default; the other framings are on the thumbnails object.
            - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
              - …
            - `tag` 'white-background' | 'gym-shot', required
            - `orientation` 'landscape' | 'portrait': Video aspect orientation. Most exercise clips are PORTRAIT (vertical, ~9:16), shot for mobile. Do not assume landscape. Read this field and size your player to a portrait container (e.g. aspect-ratio 9/16) with object-fit: cover so clips fill the frame instead of letterboxing.
            - `isPrimary` boolean, required
        - `sets` integer
        - `reps` string
        - `restSeconds` integer
        - `group` string: Block label when structure=superset ("A","B",...) or circuit ("circuit"). Absent on straight sets and unpaired exercises.
        - `groupType` 'superset' | 'circuit': Block type; absent on straight sets.
        - `groupPosition` integer: 1-based position inside the block.
        - `order` integer
      - `warmup` WorkoutExercise[]: Warmup exercises (bodyweight stretches, mobility, and light cardio) placed at the start of this day. Present unless includeWarmup=false.
        - `exercise` Exercise
          - `id` string, uuid, required
          - `title` string, required
          - `slug` string, required
          - `description` string, nullable
          - `instructions` string[], nullable
          - `importantPoints` string[], nullable
          - `muscleGroup` string, required
          - `secondaryMuscles` string[], nullable
          - `muscleGroups` string[], nullable: Every muscle this exercise works, primary and supporting, at anatomical detail (e.g. ["back","lats","rhomboids","traps","erector_spinae"]). muscleGroup is the single coarse value the filters use; this is the detail behind it, for body maps and analysis. Populated for the whole catalogue.
          - `equipment` string, required
          - `category` string, nullable
          - `difficulty` string, nullable
          - `videoUrl` string, uri, nullable: Pre-signed CDN URL for primary video. Expires after 48 hours.
          - `videoHlsUrl` string, uri, nullable: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
          - `thumbnailUrl` string, uri, nullable: Default thumbnail (up to 600x800, 3:4) for the primary video, served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default. Returned in browse mode too, where the video URLs are not.
          - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
            - `default` string, uri, required: Up to 600x800 (3:4). The one to reach for, and what thumbnailUrl points at. A mild portrait holds a standing lift and a lying floor exercise without cramping either.
            - `square` string, uri, required: Up to 600x600 (1:1). List rows, grid tiles, anywhere the layout is already square.
            - `portrait` string, uri, required: Up to 720x1280 (9:16). Matches the video itself, for full-bleed mobile and story-style screens.
            - `landscape` string, uri, required: Up to 1280x720 (16:9). Wide cards, hero strips, and the poster frame of a video player. Tall poses are cropped to the upper body to fit the shape. Sizes are ceilings: the images are never upscaled, so a crop is only as large as the source clip allows, and older clips cap around 720px wide.
            - `original` string, uri, required: The whole frame, uncropped, as shot (portrait, ~9:16). Use it when you want to crop it yourself.
          - `videoDurationSecs` integer, nullable
          - `exerciseType` string[], nullable
          - `hasVideo` boolean, required
          - `hasVideoWhite` boolean, required
          - `hasVideoGym` boolean, required
          - `videos` Video[], required
            - `id` string, uuid, required: Stable unique ID of this video. Use it to tell apart the videos of one exercise, or to store a reference to one specific clip. It does not change when the signed URLs do.
            - `videoUrl` string, uri: Pre-signed CDN URL for MP4 video. Expires after 48 hours.
            - `videoHlsUrl` string, uri: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
            - `thumbnailUrl` string, uri: The default thumbnail (up to 600x800, 3:4), served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default; the other framings are on the thumbnails object.
            - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
              - …
            - `tag` 'white-background' | 'gym-shot', required
            - `orientation` 'landscape' | 'portrait': Video aspect orientation. Most exercise clips are PORTRAIT (vertical, ~9:16), shot for mobile. Do not assume landscape. Read this field and size your player to a portrait container (e.g. aspect-ratio 9/16) with object-fit: cover so clips fill the frame instead of letterboxing.
            - `isPrimary` boolean, required
        - `sets` integer
        - `reps` string
        - `restSeconds` integer
        - `group` string: Block label when structure=superset ("A","B",...) or circuit ("circuit"). Absent on straight sets and unpaired exercises.
        - `groupType` 'superset' | 'circuit': Block type; absent on straight sets.
        - `groupPosition` integer: 1-based position inside the block.
        - `order` integer
      - `cooldown` WorkoutExercise[]: Cooldown exercises (bodyweight stretches and mobility) placed at the end of this day. Present unless includeCooldown=false.
        - `exercise` Exercise
          - `id` string, uuid, required
          - `title` string, required
          - `slug` string, required
          - `description` string, nullable
          - `instructions` string[], nullable
          - `importantPoints` string[], nullable
          - `muscleGroup` string, required
          - `secondaryMuscles` string[], nullable
          - `muscleGroups` string[], nullable: Every muscle this exercise works, primary and supporting, at anatomical detail (e.g. ["back","lats","rhomboids","traps","erector_spinae"]). muscleGroup is the single coarse value the filters use; this is the detail behind it, for body maps and analysis. Populated for the whole catalogue.
          - `equipment` string, required
          - `category` string, nullable
          - `difficulty` string, nullable
          - `videoUrl` string, uri, nullable: Pre-signed CDN URL for primary video. Expires after 48 hours.
          - `videoHlsUrl` string, uri, nullable: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
          - `thumbnailUrl` string, uri, nullable: Default thumbnail (up to 600x800, 3:4) for the primary video, served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default. Returned in browse mode too, where the video URLs are not.
          - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
            - `default` string, uri, required: Up to 600x800 (3:4). The one to reach for, and what thumbnailUrl points at. A mild portrait holds a standing lift and a lying floor exercise without cramping either.
            - `square` string, uri, required: Up to 600x600 (1:1). List rows, grid tiles, anywhere the layout is already square.
            - `portrait` string, uri, required: Up to 720x1280 (9:16). Matches the video itself, for full-bleed mobile and story-style screens.
            - `landscape` string, uri, required: Up to 1280x720 (16:9). Wide cards, hero strips, and the poster frame of a video player. Tall poses are cropped to the upper body to fit the shape. Sizes are ceilings: the images are never upscaled, so a crop is only as large as the source clip allows, and older clips cap around 720px wide.
            - `original` string, uri, required: The whole frame, uncropped, as shot (portrait, ~9:16). Use it when you want to crop it yourself.
          - `videoDurationSecs` integer, nullable
          - `exerciseType` string[], nullable
          - `hasVideo` boolean, required
          - `hasVideoWhite` boolean, required
          - `hasVideoGym` boolean, required
          - `videos` Video[], required
            - `id` string, uuid, required: Stable unique ID of this video. Use it to tell apart the videos of one exercise, or to store a reference to one specific clip. It does not change when the signed URLs do.
            - `videoUrl` string, uri: Pre-signed CDN URL for MP4 video. Expires after 48 hours.
            - `videoHlsUrl` string, uri: Pre-signed CDN URL for HLS playlist. Expires after 48 hours.
            - `thumbnailUrl` string, uri: The default thumbnail (up to 600x800, 3:4), served from ymove.app. Static and cacheable - it does not expire. Same as thumbnails.default; the other framings are on the thumbnails object.
            - `thumbnails` Thumbnails: Five framings of one frame from the clip, all served from ymove.app. Unlike the video URLs these are static: no token, no expiry, safe to cache, store and hotlink. They accompany the video whenever one is returned, and never count against your monthly cap. On Scale they are also returned without a video, in browse mode and with excludeVideos; on the capped plans they are not, so the monthly exercise limit stays visible while you build rather than arriving all at once the day you switch video on. The frame is picked from across the clip for a visible face and the most extended pose, so a jump is caught mid-air rather than at the standing rest position the clip opens on. Every crop is centred on the person by face detection, and each one is white-balanced after cropping, so a grid of clips from different shoots reads as one library instead of drifting warm or green between shoots.
              - …
            - `tag` 'white-background' | 'gym-shot', required
            - `orientation` 'landscape' | 'portrait': Video aspect orientation. Most exercise clips are PORTRAIT (vertical, ~9:16), shot for mobile. Do not assume landscape. Read this field and size your player to a portrait container (e.g. aspect-ratio 9/16) with object-fit: cover so clips fill the frame instead of letterboxing.
            - `isPrimary` boolean, required
        - `sets` integer
        - `reps` string
        - `restSeconds` integer
        - `group` string: Block label when structure=superset ("A","B",...) or circuit ("circuit"). Absent on straight sets and unpaired exercises.
        - `groupType` 'superset' | 'circuit': Block type; absent on straight sets.
        - `groupPosition` integer: 1-based position inside the block.
        - `order` integer
    - `notes` string
  - `_warning` MonthlyCapWarning: Included as `_warning` in the response body when the monthly exercise cap is exceeded. Exercises not previously accessed will have their video properties (videoUrl, videoHlsUrl, thumbnailUrl, videoDurationSecs, videos) removed entirely from the response. Previously accessed exercises keep their video URLs.
    - `message` string
    - `reason` 'monthly_exercise_cap'
    - `monthlyExercisesUsed` integer
    - `monthlyExerciseLimit` integer
    - `upgradeUrl` string

## Other responses

- `401`: Invalid or missing API key
- `429`: Rate limit exceeded

---

[API](https://skmtc.dev/ymove/apis/ymove-fitness-api.md) · [All operations](https://skmtc.dev/ymove/apis/ymove-fitness-api/llms.txt) · [OpenAPI document](https://skmtc.dev/ymove/apis/ymove-fitness-api/revisions/6636f2b4a343?raw)
