Search Queries
List endpoints across services share one query grammar for full-text search,
filtering, sorting, and pagination. Any endpoint whose reference page shows
the q / filter / sort_by parameters speaks it. (A few endpoints have
bespoke list parameters instead - /api/v1/kv/search takes q/prefix/
tags - their reference pages show exactly what they accept.)
GET /api/v1/guilds?q=dragon&filter=visibility:eq:public&sort_by=created_at&sort_order=desc&page_size=2| Parameter | Type | Description |
|---|---|---|
q | string | Full-text search across the entity's searchable fields |
filter | string, repeatable | field:operator:value filters, ANDed together |
sort_by | string | Field to sort by (per-entity allowlist) |
sort_order | string | asc or desc; defaults per field |
page | integer | 1-indexed; page=0 is rejected with a 400, not clamped |
page_size | integer | Default 25, clamped to 1-100 |
Filters
Filters are field:operator:value. Multiple filter parameters are ANDed;
multiple values inside one eq (split on | or ,) are ORed.
| Operator | Meaning | Example |
|---|---|---|
eq | Equals; OR-list with | | status:eq:active|pending |
range | Inclusive numeric range, either bound optional | member_count:range:10, |
prefix | Text prefix | path:prefix:config/ |
contains | Every query token occurs, any order | description:contains:error |
exact | Adjacent-token phrase | name:exact:John Doe |
fuzzy | Typo-tolerant | name:fuzzy:jonh |
Field-type notes, all enforced with 400 rather than silently ignored:
- Booleans use
eqwith exactlytrueorfalse(private:eq:false). Other operators on a boolean field are rejected. - Timestamps filter as epoch milliseconds on the logical field name:
created_at:range:1704067200000,. - Text matching follows the search index's tokens, not raw bytes:
punctuation separates tokens, so
exactmeans token adjacency (a@b,a b, anda-bare the same phrase) andcontainsANDs tokens without preserving order. When a value must match as one opaque scalar, the entity models it as a tag field and you useeq. - Filter field names are case-sensitive; operator tokens are not.
sort_bynames match case-insensitively against the entity's allowlist.
Full-text search
q tokenizes the query and prefix-matches each token across the entity's
searchable fields - q=dragon knights finds "Dragon Knights Elite" and
"Knighthood of Dragons". Combine freely with filters.
A real query
Executed against a live server (guild service, envelope trimmed):
GET /api/v1/guilds?q=dragon&filter=visibility:eq:public&sort_by=created_at&sort_order=desc&page_size=2{
"status": 200,
"msg": "OK",
"data": {
"guilds": [
{ "guild_id": "...", "name": "Dragon Riders", "visibility": "public", "created_at": "2026-08-28T03:41:56.493756Z" },
{ "guild_id": "...", "name": "Dragon Knights", "visibility": "public", "created_at": "2026-08-28T03:41:56.421591Z" }
],
"pagination": {
"total": 2,
"page": 1,
"page_size": 2,
"has_more": false
}
}
}The items key is named after the entity (guilds, webhooks, ...); the
pagination shape is identical everywhere.
Errors
Bad queries return 400 with a specific message - never a silently empty
result:
| Message | Cause |
|---|---|
Invalid filter syntax: {raw} | Not exactly three field:operator:value parts |
Unsupported filter operator: {op} | Unknown operator token |
Unknown filter field: {field} | Field not filterable on this entity; names are case-sensitive |
Invalid boolean value for {field}: ... | Boolean value not exactly true/false |
{field} filter only supports eq operator | Wrong operator for a tag or boolean field |
Invalid numeric value/bound: {value} | Non-numeric value on a numeric field |
Unsupported sort field: {name} | sort_by not in the entity's allowlist |
Which fields are filterable and sortable is entity-specific - each list endpoint's page in the API reference documents its parameters.