Narrowing a listing
Every listing, on both APIs, takes the same four things: filter[...], sort, include and per_page. What differs is only which filters, sorts and includes a given endpoint accepts, and each endpoint's reference page lists them, with their valid values wherever the set is closed.
Filtering
curl "https://api.tibia.dev/v1/creatures?filter%5Bis_boss%5D=true&filter%5Btype%5D=dragons"
curl "https://huntera.tibia.dev/v1/creatures?filter%5Bweak_to%5D=holy"Filters combine with AND. There are five kinds:
| Kind | Example | Notes |
|---|---|---|
| Text | filter[name]=dragon | Partial, case-insensitive |
| Boolean | filter[is_boss]=true | true or false; anything else is a 400 |
| Closed set | filter[bestiary_level]=hard | The reference page lists every accepted value; anything else is a 400 |
| Vocabulary | filter[type]=dragons | By slug or by the name the wiki prints; a value nothing carries finds nothing |
| Range | filter[min_mana]=100 | A min_ or max_ bound, inclusive; a bound that is not a number is a 400 |
Most filters show their values in the reference. Where a filter names a fixed vocabulary (a creature class, an element, an imbuement type) the API Reference renders it as a dropdown, so you never have to guess a slug. The ones left open are the ones that name an item or a creature, where there are thousands and the listing endpoint is the better way to browse.
Sorting
curl "https://api.tibia.dev/v1/creatures?sort=-experience"A leading - reverses. Comma-separate for tie-breaks: sort=-experience,name. Whatever you sort by, the order ends on a unique column, so no row repeats or goes missing between pages.
Some sorts follow a game ladder rather than the alphabet: sort=bestiary_level runs Harmless → Challenging, because alphabetical would make Challenging the easiest thing in the game.
Including relations
A listing is about its own rows, so relations are opt-in:
curl "https://api.tibia.dev/v1/creatures?filter%5Btype%5D=dragons&include=drops.item,abilities.ability"
curl "https://huntera.tibia.dev/v1/items?filter%5Bdropped_by%5D=jaul&include=droppedByBosses"A detail route answers every relation it takes part in without being asked, so include is for listings, where dragging six more tables behind every row would be expensive.
Pagination
per_page defaults to 25 and caps at 100. Responses carry links and meta:
{
"data": [ ... ],
"links": { "first": "...", "last": "...", "prev": null, "next": "https://api.tibia.dev/v1/creatures?page=2" },
"meta": { "current_page": 1, "last_page": 88, "per_page": 25, "total": 2193 }
}Follow links.next rather than incrementing page yourself; it carries your filters and sort along with it.
A filter the endpoint does not know is a 400
Nothing is silently ignored. Misspell a filter and the API answers 400 naming every one it does accept:
{
"message": "Requested filter(s) `banana` are not allowed. Allowed filter(s) are `name, status, is_boss, bestiary_level, bestiary_occurrence, bosstiary_class, spawns_in_raids, class, type, bestiary_class, ability, immune_to, weak_to, drops, quest, hunt, raid`."
}The same holds for an unknown sort or include, and for a value a filter cannot read: a boolean that is neither true nor false, a bound that is not a number, a value outside a closed set. This is deliberate: a typo that quietly returns an unfiltered page is a bug you find in production, and one that returns 400 is a bug you find immediately.
Updated about 2 months ago