Migration Guide April 29, 2026 · 8 min read

Migrating from ExerciseDB to WorkoutX: Complete Guide with Code Examples

ExerciseDB free tier limits hit? Tired of routing through RapidAPI? This guide walks you through migrating to WorkoutX in under 30 minutes — full endpoint mapping, auth changes, and drop-in code replacements.

verified Published by the WorkoutX team — builders of the exercise API used by fitness app developers. Learn more

Migrating from ExerciseDB to WorkoutX API Cover Image - WorkoutX Blog
bolt Quick Summary (TL;DR)

Migrating from ExerciseDB to WorkoutX takes about 20-30 minutes: swap the RapidAPI headers for a single X-WorkoutX-Key, remap endpoint paths (e.g. /exercises/bodyPart/{bodyPart} → /v2/exercises?bodyPart={bodyPart}), replace gifUrl images with the media.mp4 video, and adjust for minor JSON field-casing differences. WorkoutX's free tier (500 requests in total) requires no credit card, so you can test the migration before switching production traffic.

Why Developers Are Leaving ExerciseDB

ExerciseDB has been the default exercise API recommendation since 2021 — it's mentioned in hundreds of YouTube tutorials and GitHub repos. But as apps grow, developers consistently run into the same problems:

  • RapidAPI dependency: Every request routes through RapidAPI's infrastructure, adding latency and an extra failure point
  • Rate limit surprises: The free tier limits (10 req/min) are hit fast in any real app
  • Pricing opacity: RapidAPI pricing changes without notice, and costs scale unpredictably
  • Missing data fields: No calorie burn data, no difficulty levels, no exercise mechanics — data most fitness apps actually need (e.g., knowing a barbell bent-over row is intermediate difficulty and a compound pull)
  • No developer portal: You manage billing through RapidAPI, not through the API provider directly

Good news: WorkoutX is a direct replacement. The exercise data structure is similar, every ExerciseDB endpoint has a WorkoutX equivalent, and the migration takes about 20–30 minutes including testing.

Endpoint Mapping

Here's every ExerciseDB endpoint and its WorkoutX v2 equivalent. ExerciseDB's path-style filters become query parameters, and you can combine them in one request:

ExerciseDB (RapidAPI)
GET /exercises
→
WorkoutX
GET /v2/exercises
GET /exercises/{id}
→
GET /v2/exercises/{id}
GET /exercises/bodyPart/{bodyPart}
→
GET /v2/exercises?bodyPart={bodyPart}
GET /exercises/target/{target}
→
GET /v2/exercises?target={target}
GET /exercises/equipment/{type}
→
GET /v2/exercises?equipment={type}
GET /exercises/name/{name}
→
GET /v2/exercises?name={name}
GET /bodyPartList
→
GET /v2/bodyParts
GET /targetList
→
GET /v2/targets
GET /equipmentList
→
GET /v2/equipment

WorkoutX also adds things ExerciseDB doesn't have:

  • GET /v2/exercises?target=Glutes&equipment=Dumbbell&difficulty=beginner — combine any filters (also category, mechanic, force, muscle, model) in one request
  • GET /v2/exercises/{id}/media — fresh MP4 and poster links for one exercise
  • GET /v2/exercises/{id}/similar and /alternatives — ranked substitutes, including "same muscle, different equipment"
  • GET /v2/exercises/{id}/calories?weightKg=80&minutes=15 — calorie estimate for a body weight and duration
  • GET /v2/categories and GET /v2/muscles — the remaining filter values, with exercise counts
  • ?model=female and counterpartId — 843 exercises come with both a male and a female model
  • ?lang= — names, instructions and tips in Turkish, German, Spanish, French and Chinese (filters stay in English)

Exercise ids are different too: WorkoutX uses ids like ex_SAWOOH. If your app stores ExerciseDB ids in workouts or favorites, look each exercise up once (for example with ?name=) and save the WorkoutX id.

Authentication Change

ExerciseDB requires two RapidAPI headers. WorkoutX uses a single header with your API key (v2 rejects a key in the URL, such as ?api-key=, with a 400):

ExerciseDB (RapidAPI)
X-RapidAPI-Key: YOUR_RAPIDAPI_KEY
X-RapidAPI-Host: exercisedb.p.rapidapi.com
WorkoutX
X-WorkoutX-Key: YOUR_WORKOUTX_KEY

Get your free WorkoutX API key at workoutxapp.com — no credit card required, 500 free requests.

Base URL Change

Before
https://exercisedb.p.rapidapi.com
After
https://api.workoutxapp.com/v2

JavaScript Migration

If you're using the ExerciseDB fetch pattern, here's the before/after:

Before — ExerciseDB
const response = await fetch(
  'https://exercisedb.p.rapidapi.com/exercises/bodyPart/chest?limit=10',
  {
    headers: {
      'X-RapidAPI-Key': 'YOUR_RAPIDAPI_KEY',
      'X-RapidAPI-Host': 'exercisedb.p.rapidapi.com',
    },
  }
);
const exercises = await response.json();
After — WorkoutX
const response = await fetch(
  'https://api.workoutxapp.com/v2/exercises?bodyPart=chest&limit=10',
  {
    headers: {
      'X-WorkoutX-Key': 'YOUR_WORKOUTX_KEY',
    },
  }
);
const { results: exercises } = await response.json();
// WorkoutX wraps results in { total, limit, offset, results: [...] }

Note on response shape: WorkoutX wraps paginated results in { total, limit, offset, results: [...] }. ExerciseDB returns a bare array. Update any code that does response.json() directly to response.json().then(r => r.results) for list endpoints. limit is capped at 10 on the Free plan, 100 on Starter and 200 on other plans.

Response Shape Comparison

Here's a side-by-side comparison using the barbell bench press as an example exercise:

ExerciseDB response (per exercise)
{
  "id": "0001",
  "name": "Barbell Bench Press",
  "bodyPart": "chest",
  "target": "pectorals",
  "equipment": "barbell",
  "gifUrl": "https://v2.exercisedb.io/image/...",
  "instructions": ["Step 1...", "Step 2..."],
  "secondaryMuscles": ["triceps", "shoulders"]
}
WorkoutX response (per exercise)
{
  "id": "ex_SAWOOH",
  "name": "Barbell Bench Press",
  "bodyPart": "Chest",
  "target": "Pectorals",
  "equipment": "Barbell",
  "instructions": ["Step 1...", "Step 2..."],
  "secondaryMuscles": [
    { "group": "Shoulders", "anatomical": ["Deltoids"] },
    { "group": "Triceps", "anatomical": ["Triceps Brachii"] }
  ],
  "media": {
    "expiresAt": "...",
    "mediaVersion": 1,
    "watermarked": false,
    "mp4": "https://…/m/v1/ex_SAWOOH/mp4-720.mp4?...",
    "poster": "https://…/m/v1/ex_SAWOOH/poster.jpg?..."
  },

  // Extra fields ExerciseDB doesn't have:
  "model": "male",
  "counterpartId": "ex_HYOSAB",
  "tips": ["Keep your feet flat on the floor...", "..."],
  "difficulty": "intermediate",
  "category": "strength",
  "mechanic": "compound",
  "force": "push",
  "met": 5,
  "caloriesPerMinute": 6.1,
  "recommendedSets": "3-4",
  "recommendedReps": "6-10",
  "description": "Full exercise description..."
}

Python Migration

Before — ExerciseDB
import requests

url = "https://exercisedb.p.rapidapi.com/exercises/bodyPart/chest"
headers = {
    "X-RapidAPI-Key": "YOUR_RAPIDAPI_KEY",
    "X-RapidAPI-Host": "exercisedb.p.rapidapi.com"
}
response = requests.get(url, headers=headers, params={"limit": 10})
exercises = response.json()  # bare list
After — WorkoutX
import requests

url = "https://api.workoutxapp.com/v2/exercises"
headers = {
    "X-WorkoutX-Key": "YOUR_WORKOUTX_KEY"
}
response = requests.get(url, headers=headers, params={"bodyPart": "chest", "limit": 10})
result = response.json()
exercises = result["results"]  # { total, limit, offset, results: [...] }

From GIF URLs to MP4 Video

ExerciseDB returns a gifUrl for each exercise. WorkoutX v2 doesn't serve GIFs: each exercise has a media object with a 600×720 MP4 animation (30 fps, typically 100–300 KB) and a JPEG poster. A GIF at the same quality is several times larger, and every platform plays MP4 natively. Replace your <img src={gifUrl}> with a muted, looping video:

<video src="{media.mp4}" poster="{media.poster}" autoplay loop muted playsinline></video>

The links are signed and expire after 24–25 hours (media.expiresAt), so don't save them. Store the exercise id and get fresh links from the exercise response or GET /v2/exercises/{id}/media when you show it. Downloading the files or serving them from your own servers or CDN is not permitted on any plan. On the Free plan, media is a watermarked 300×360 preview.

Field Casing Difference

One small difference: ExerciseDB returns field values in lowercase ("chest", "barbell"). WorkoutX returns them in Title Case ("Chest", "Barbell"). Update any string comparisons in your UI:

Before
if (exercise.bodyPart === 'chest') { ... }
if (exercise.equipment === 'barbell') { ... }
After
if (exercise.bodyPart === 'Chest') { ... }
// OR use case-insensitive comparison to be safe:
if (exercise.bodyPart.toLowerCase() === 'chest') { ... }

Query filters are case-insensitive, so ?bodyPart=chest and ?bodyPart=Chest return the same results.

Migration Checklist

  • ✅ Sign up at workoutxapp.com and copy your API key
  • ✅ Replace base URL: exercisedb.p.rapidapi.com → api.workoutxapp.com/v2
  • ✅ Replace headers: two RapidAPI headers → one X-WorkoutX-Key header
  • ✅ Move path filters to query parameters: /exercises/bodyPart/chest → /v2/exercises?bodyPart=chest
  • ✅ Update response parsing: bare array → response.results
  • ✅ Replace gifUrl images with media.mp4 videos and media.poster
  • ✅ Store WorkoutX exercise ids (ex_…), not media URLs
  • ✅ Update string comparisons: lowercase → Title Case field values
  • ✅ (Optional) Use new fields: difficulty, caloriesPerMinute, mechanic, tips, model
  • ✅ Remove RapidAPI account dependency

Pricing Comparison

Monthly Requests WorkoutX ExerciseDB (RapidAPI)
Up to 500 Free (500 requests in total) Free (10 req/min cap)
Up to 3,000 $19/mo (Starter, 5,000) ~$10–15/mo (varies)
Up to 10,000 $39/mo (Growth, 25,000) ~$25–35/mo
Up to 35,000 $79/mo (Scale, 100,000) ~$50+/mo

The WorkoutX Free plan is 500 requests in total, not per month. Every paid plan gets the same 720p media; the plans differ by quota. See pricing for media limits per plan.

Ready to migrate? Get your free WorkoutX API key at workoutxapp.com — 500 requests in total, watermarked video previews included, no credit card required.

swap_horiz

Migrate in 20 minutes — it's free

500 free requests, direct API access, no RapidAPI dependency.

Get Free API Key arrow_forward