KinesteX

Filtering & Parameters

These rules hold on all three list endpoints, so you only learn them once.


Which filter each catalog accepts


FilterExercisesWorkoutsPlans
searchYesYesYes
body_partsYesYesYes
categoriesYesNoNo
categoryNoYes (single value)No
difficulty_level (alias dif_level)YesNoNo
dif_levelYes (alias)Yes (single value)No
levelNoNoYes (integer)
translation_languagesYesYesYes
remove_inactiveYesNoNo
include_exercisesNoYesNo
include_weeksNoNoYes
include_shared_libraryYesYesYes
langYesYesYes
limit / offsetYesYesYes

Array parameters: three interchangeable forms


text
1?body_parts=Chest,Triceps                 // comma-separated
2?body_parts=Chest&body_parts=Triceps      // repeated
3?body_parts[]=Chest&body_parts[]=Triceps  // bracketed

Surrounding whitespace is trimmed, so ?body_parts=Chest, Triceps works. Multi-word values stay intact: ?body_parts=Lower Back,Full Body is two body parts, not four (URL-encode the space as %20).


body_parts, categories, difficulty_level, and translation_languages are arrays. category and dif_level on the workout list are single values: a comma in them is matched literally, so ?dif_level=easy,medium looks for a difficulty named "easy,medium" and returns an empty page. Issue one request per value and merge client-side.


Combining filters


  • Within one filter, values are OR. categories=Strength,Cardio/Endurance returns exercises in either category.
  • Across filters it is AND. body_parts=Chest&difficulty_level=easy returns easy chest exercises.
  • There is no "match all" mode. body_parts=Chest,Triceps means chest or triceps, never both.
  • Every name filter is case-insensitive. easy, Easy, and EASY are the same request.

Values that match nothing


Name filters compare on string equality, so an unrecognized value is not an error. You get 200 with an empty array and pagination.total: 0. The one exception is ?level= on the plan list, which must be an integer and returns 400 otherwise.


Empty value means no filter. Sending ?body_parts= or ?categories= applies no filter and returns the full unfiltered page. If your UI builds query strings from optional fields, omit the parameter entirely rather than sending it blank.

Booleans must be literal


include_shared_library, remove_inactive, include_exercises, include_weeks, and include_audio accept true / false (also 1 / 0, t / f). Anything else, such as yes or on, is read as false without an error. include_shared_library=yes silently hides the entire shared library.


Search is matched against English titles


search is a case-insensitive partial match on the English title, whatever lang you send. ?search=flexiones&lang=es returns nothing; search for push and read the Spanish titles off the result.


Result ordering


Newest first by creation date. When search is present, results are ranked by match quality first (exact title, then prefix, then partial) and creation date second.


Allowed values


Body parts (18), shared by all three catalogs:


text
1Neck            Shoulders   Chest       External Oblique
2Abs             Biceps      Triceps     Forearms
3Traps           Lats        Lower Back  Glutes
4Quads           Hamstrings  Abductors   Adductors
5Calves          Full Body

Exercise categories (8):


text
1Strength        Muscle Gain          Weight Loss   Cardio/Endurance
2General Fitness Wellness/Flexibility Warm Up       Cooldown

Exercise difficulty (3): easy, medium, hard. Returned lowercase on exercises, accepted in any casing everywhere.


Workout category is free text set per workout rather than a fixed list (values in use include Fitness, Cardio, Strength, HIIT). Workout dif_level is stored capitalized (Easy, Medium, Hard) and returned as stored, so compare case-insensitively on your side. Read an unfiltered first page to build a picker rather than hard-coding either list.


The same filters work on the JWT routes. /api/exercises, /api/workouts, and /api/plans accept every parameter listed here and return the same data plus internal fields. The one difference: they spell the library toggle include_kinestex_library instead of include_shared_library.