Skip to content

Filtering

http
GET /books?filter[title][eq]=Dune

The general form is filter[<field>][<operator>]=<value>. Multiple filter[...] params AND together, and multiple operators on the same field also AND together:

http
GET /books?filter[pages][gte]=200&filter[pages][lt]=500

Operators

OperatorWire tokenExample
Equalseqfilter[status][eq]=active
Not equalsnefilter[status][ne]=banned
Greater/equalgt / gtefilter[age][gte]=18
Less/equallt / ltefilter[age][lt]=65
In listinfilter[status][in]=active,pending
Not in listnotInfilter[role][notIn]=bot,test
Likelikefilter[name][like]=%25john%25
Case-insensitive likeilikefilter[name][ilike]=%25john%25
Betweenbetweenfilter[createdAt][between]=2026-01-01,2026-06-01
Is nullisNullfilter[deletedAt][isNull]=true
Is not nullisNotNullfilter[deletedAt][isNotNull]=true

Wire tokens are exact case, so gte works but GTE does not. A misspelled or wrong-case operator returns a 400 instead of being silently ignored.

like and ilike never auto-wrap wildcards: pass % yourself, and escape any literal % or _ in the value with a backslash. Both apply to string columns only.

in and notIn also accept the repeated-key form instead of a comma list:

http
GET /books?filter[status][in][]=active&filter[status][in][]=pending

between takes exactly two comma-separated bounds. isNull and isNotNull are boolean-valued: isNull=false means the same thing as isNotNull=true, so pick whichever reads better.

Which fields you can filter on

Only fields on the entity's filter.fields allowlist can be filtered on. See Allowed for how to configure that list. Filtering on anything outside it returns a 400, never a silent no-op.

OR, NOT, and nested logic

The same bracket grammar covers or and not, and it can nest arbitrarily deep, up to filter.limits.maxDepth (default 3):

http
GET /books?filter[or][0][author][eq]=Tolkien&filter[or][1][author][eq]=Herbert
GET /books?filter[not][status][eq]=banned

For anything the bracket grammar gets awkward at, filter also accepts one JSON-encoded value as a full-power escape hatch. It parses into the same filter tree as the bracket form. If both are present on a request, they AND together:

http
GET /books?filter={"or":[{"author":{"eq":"Tolkien"}},{"not":{"status":{"eq":"banned"}}}]}

Filtering across relations

Relation-path filters use dot notation and restrict root rows, without loading the related collection:

http
GET /books?filter[author.country][eq]=UK

This never filters what's inside an included relation. It only decides which root rows come back.

Limits

Every request is guarded by limits, configurable per scope:

  • filter.limits.maxDepth (default 3) caps how deeply or/not can nest.
  • filter.limits.maxInValues (default 100) caps in/notIn array length.
  • filter.limits.maxLikePatternLength (default 200) caps like/ilike pattern length.
  • pagination.maxLimit (default 100) caps page size.

If a request breaks several of these at once (filter, sort, select, pagination), Kavo collects the violations and reports them together in a single response. See Errors.