MuscleLib API is a bilingual (English/Portuguese) RESTful API for searching and listing fitness exercises. It provides fuzzy search, advanced filtering, pagination, field selection, and high-quality exercise images — all in a single API.
Built with Node.js and Express.js, the API uses MongoDB for storage and Fuse.js for intelligent search.
The API serves exercise data in two languages (en and pt). Every exercise has a bilingual data structure — when you make a request, the API automatically localizes the response to your preferred language.
Language detection priority:
langquery parameter (?lang=pt)Accept-LanguageHTTP header- Falls back to English (
en)
Search uses fuzzy matching via Fuse.js, so you don't need exact spelling. Filters are validated against the actual values present in the database, and images are served directly from the filesystem.
- Exercise Listing — Paginated list with configurable page and limit
- Fuzzy Search — Typo-tolerant search via Fuse.js
- Bilingual — Native support for English and Portuguese (PT-BR)
- Advanced Filters — Filter by force, level, equipment, category, and muscles
- Field Selection — Request only the fields you need to reduce payload
- Exercise Images — Two high-quality images per exercise
- Filter Options — Discover available filter values via
/filtersendpoint - Health Check — Simple
/api/pingendpoint - Statistics — Exercise and image count via
/stats
https://musclelib-api.vercel.app
All endpoints below use this base URL. Replace with
http://localhost:5000for local development.
GET /api/pingResponse: pong (200)
GET /api/exercisesReturns paginated exercises matching the applied filters.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
lang |
string |
Accept-Language or "en" |
Language: "en" or "pt" |
page |
integer |
0 |
Page number (zero-indexed) |
limit |
integer |
50 |
Items per page |
fields |
string |
all fields | Comma-separated fields to return |
force |
string |
— | Filter by force type |
level |
string |
— | Filter by level |
equipment |
string |
— | Filter by equipment |
category |
string |
— | Filter by category |
primaryMuscles |
string |
— | Filter by primary muscle |
secondaryMuscles |
string |
— | Filter by secondary muscle |
Valid fields values: name, force, level, mechanic, equipment, primaryMuscles, secondaryMuscles, instructions, category, images.
Example:
curl "https://musclelib-api.vercel.app/api/exercises?lang=en&page=0&limit=5&force=pull"Response (200):
[
{
"_id": "677471841533c77a3a55b4ff",
"id": "Barbell_Curl",
"name": "Barbell Curl",
"force": "pull",
"level": "beginner",
"mechanic": "isolation",
"equipment": "barbell",
"primaryMuscles": ["biceps"],
"secondaryMuscles": ["forearms"],
"instructions": [
"Stand up straight with your feet shoulder-width apart...",
"Curl the barbell up towards your chest..."
],
"category": "strength",
"images": ["Barbell_Curl/0.jpg", "Barbell_Curl/1.jpg"]
}
]Response (404 — no results):
{
"message": "No exercises found."
}GET /api/exercises/search?query={term}Performs a fuzzy search against exercise names.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
query |
string |
required | Search term |
lang |
string |
Accept-Language or "en" |
Language |
fields |
string |
all fields | Comma-separated fields to return |
Example:
curl "https://musclelib-api.vercel.app/api/exercises/search?query=squat&lang=en"Response (200):
{
"exercises": [
{
"_id": "677471841533c77a3a55b4ff",
"id": "Barbell_Back_Squat",
"name": "Barbell Back Squat",
"force": "push",
"level": "intermediate",
"primaryMuscles": ["quadriceps"],
"images": ["Barbell_Back_Squat/0.jpg", "Barbell_Back_Squat/1.jpg"]
}
]
}Response — when no translation exists for the selected language:
{
"message": "Exercise not available in the selected language. Try: Barbell Back Squat"
}GET /api/exercises/filtersReturns all available filter values aggregated from the database.
Query Parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
lang |
string |
Accept-Language or "en" |
Language |
Example:
curl "https://musclelib-api.vercel.app/api/exercises/filters?lang=en"Response (200):
{
"force": ["push", "pull", "static"],
"level": ["beginner", "intermediate", "expert"],
"category": ["barbell", "dumbbell", "machine", "cables", "body only", ...],
"equipment": ["barbell", "dumbbell", "body only", "cable", "ez-bar", ...],
"primaryMuscles": ["biceps", "triceps", "quadriceps", "chest", "back", ...],
"secondaryMuscles": ["forearms", "glutes", "hamstrings", "abs", ...]
}GET /api/exercises/{exerciseName}/{imageIndex}.jpgServes an exercise image directly. Each exercise has two images (0.jpg and 1.jpg).
Parameters:
| Parameter | Type | Description |
|---|---|---|
exerciseName |
string |
Exercise ID (e.g., Barbell_Curl) |
imageIndex |
integer |
0 or 1 |
Example:
curl "https://musclelib-api.vercel.app/api/exercises/Barbell_Curl/0.jpg" -o image.jpgResponse: JPEG image (200) or JSON error (400).
GET /statsReturns total exercise and image counts.
Example:
curl "https://musclelib-api.vercel.app/stats"Response (200):
{
"totalExercises": 1000,
"totalImages": 2000
}GET /docsRedirects to the external API documentation site.
{
"_id": ObjectId, // Auto-generated
"id": String, // Unique slug (e.g. "Barbell_Curl")
"name": {
"en": String, // Name in English
"pt": String // Name in Portuguese
},
"force": { "en": String, "pt": String },
"level": { "en": String, "pt": String },
"mechanic": { "en": String, "pt": String },
"equipment": { "en": String, "pt": String },
"primaryMuscles": { "en": [String], "pt": [String] },
"secondaryMuscles": { "en": [String], "pt": [String] },
"instructions": { "en": [String], "pt": [String] },
"category": { "en": String, "pt": String },
"images": [String] // e.g. ["Barbell_Curl/0.jpg", "Barbell_Curl/1.jpg"]
}When returned via the API, bilingual fields are flattened to the requested language:
{
"_id": String, // MongoDB ObjectId
"id": String, // Unique exercise slug
"name": String, // Localized name
"force": String, // Localized force type
"level": String, // Localized level
"mechanic": String, // Localized mechanic type
"equipment": String, // Localized equipment
"primaryMuscles": [String],
"secondaryMuscles": [String],
"instructions": [String],
"category": String,
"images": [String] // Image paths
}All error responses follow a consistent JSON structure:
{
"message": "Error description in the requested language"
}Common HTTP Status Codes:
| Status | Meaning |
|---|---|
200 |
Success |
400 |
Invalid parameters (language, fields, pagination, filter values) |
404 |
No exercises found matching the criteria |
500 |
Internal server error |
Bilingual error messages — all messages are returned in the language detected from your request.
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Watch mode
npm test -- --watchnpm start # Start the server in production
npm run dev # Start in development mode with hot-reload
npm test # Run tests with Jest
npm run lint # Validate code with ESLint
npm run format # Format code with PrettierThis project is licensed under the MIT License — see the LICENSE file for details.