Exercise search API: queries and 6 languages
2026-10-02 · 6 min
GET /v1/exercises/search?q= runs a full-text search over each exercise's name, target muscle and equipment. It matches whole words only, requires every word you send, and ranks results by relevance. The language of the response is a separate choice: the lang parameter wins, then the Accept-Language header, then English, across six languages (en, fr, es, pt, zh, ja). Because the search index is built from the English text, send English search terms and use lang to control how the results are displayed.
In short
- Search covers name, target and equipment only, not instructions or secondary muscles.
- Whole words, all required: "squat" does not match "squats", and "squat dumbbell" needs both words.
- Language order: ?lang= first, then Accept-Language, then English. Unsupported values fall back to English without an error.
- lang changes how results are shown. It does not translate your search terms, and filters such as bodyPart still take the English value.
- Responses carry Content-Language and Vary: Accept-Language, so cache per language.
How does the exercise search endpoint work?
Send q with an API key. A request without q returns a 400 problem response. Results come back in the same { data, pagination } wrapper as the list endpoint, ranked by relevance with ties broken by id. Pages default to 50 results and are capped at 100.
curl -H "Authorization: Bearer fed_live_YOUR_KEY" \
"https://api.fitexercisedb.com/v1/exercises/search?q=squat&pageSize=10&lang=fr"Each result has the full set of fields described in the response fields reference. Under the hood this is a PostgreSQL full-text query over the exercise name, target muscle and equipment; nothing else is indexed.
Why do "squat" and "squats" give different results?
The index uses PostgreSQL's simple text configuration, which lowercases words but does no stemming, and the query is built with plainto_tsquery, which requires every word. So a query matches whole words only, and every word you send must be present.
| Query | What it does |
|---|---|
q=squat | Exercises with the word squat in the name, target or equipment |
q=squat dumbbell | Only exercises that contain both words |
q=squ | Nothing for a partial word: there is no prefix matching, so this is not an autocomplete endpoint |
q=abs | Matches the target muscle Abs as well as any name containing abs |
If you need typeahead, load the taxonomy and names you care about once, cache them, and filter locally, then use search for the final lookup. Try queries against the exercise database to see what the data contains before you code.
Does search work with French, Spanish or Japanese words?
Not by translating your query. The searchable text is the English name, target and equipment, and the translated names are not part of the index, so a French word only matches when it happens to be spelled the same in English. The lang parameter changes what comes back, not what is matched.
The pattern that works for a localized search box is to translate the user's words to English terms yourself, then ask the API for text in their language:
// The user types in French. You map it to English terms, then ask for French text back.
const q = toEnglish("développé couché"); // your own mapping: "bench press"
const url = `/v1/exercises/search?q=${encodeURIComponent(q)}&lang=fr`;For common cases skip free text altogether: offer body part, muscle and equipment dropdowns, keep the English value as the key and show a translated label.
How does the API pick the response language?
Three sources are checked in order. The first supported one wins, and nothing here is an error:
| Order | Source | Example |
|---|---|---|
| 1 | The lang query parameter | ?lang=fr |
| 2 | The Accept-Language header, read left to right | Accept-Language: fr-FR,fr;q=0.9,en;q=0.8 gives fr |
| 3 | Default | en |
Region suffixes are dropped, so fr-FR and pt-BR resolve to fr and pt. The header is scanned in the order written and the first supported tag is used; quality weights are not compared. A value such as de is not supported and quietly gives English. Every response says which language it used:
Content-Language: fr
Vary: Accept-LanguageBecause the same URL can return different text per Accept-Language, put the language in your cache key or send an explicit lang.
What is translated, and what stays in English?
| Data | Behaviour with lang |
|---|---|
| bodyPart, target, equipment, secondaryMuscles | Always translated from fixed vocabularies (10 body parts, 19 target muscles) |
| name, instructions | Translated per exercise, with an English fallback when a translation is missing |
| id, met, calorie fields, category, difficulty, mechanic, force | Never changed |
| Filter values (bodyPart, target, equipment) and q | English only |
That last row is the trap. A response in French shows Poitrine for the chest, but the filter expects Chest:
# the filter value stays English, the labels come back in French
curl -H "Authorization: Bearer fed_live_YOUR_KEY" \
"https://api.fitexercisedb.com/v1/exercises?bodyPart=Chest&lang=fr"Fetch the filter lists once without lang and keep those values as keys; the exact lists are in the docs.
FAQ
- Does exercise search look at instructions or secondary muscles?
- No. The index covers name, target muscle and equipment. To filter by secondary muscle, fetch results and filter the secondaryMuscles array in your own code.
- What happens if I send an unsupported lang such as de?
- The API falls back to English and returns 200. Read the language field in each exercise, or the Content-Language header, to see which language you actually got.
- Do I need Accept-Language if I already send ?lang?
- No. The lang query parameter takes precedence, so Accept-Language is only used when lang is absent or unsupported.
- Why do my cached responses show the wrong language?
- The same URL can return different text depending on Accept-Language. Responses send Vary: Accept-Language, so make your cache honor it or add lang to the URL.