Skip to content

Filtering

Filter records by field values using bracket notation in query parameters. Filters work with pagination, sorting, relationship expansion, and field selection.

Syntax

?filter[field]=value              # Equality (default)
?filter[field.operator]=value     # With operator

Multiple filters are combined with AND logic:

?filter[status]=active&filter[age.gte]=18

Operators

OperatorSyntaxDescriptionExample
eqfilter[field]=valueEqual to (default)filter[status]=active
neqfilter[field.neq]=valueNot equal tofilter[status.neq]=inactive
gtfilter[field.gt]=valueGreater thanfilter[age.gt]=18
gtefilter[field.gte]=valueGreater than or equalfilter[age.gte]=18
ltfilter[field.lt]=valueLess thanfilter[price.lt]=100
ltefilter[field.lte]=valueLess than or equalfilter[price.lte]=100
likefilter[field.like]=valueContains substring (case-sensitive)filter[name.like]=Ali
infilter[field.in]=a,b,cIn list (comma-separated)filter[status.in]=active,pending
notInfilter[field.notIn]=a,bNot in list (comma-separated)filter[status.notIn]=archived,void
betweenfilter[field.between]=a,bInclusive range, exactly two boundsfilter[age.between]=18,65
isNullfilter[field.isNull]=Is nullfilter[email.isNull]=
isNotNullfilter[field.isNotNull]=Is not nullfilter[email.isNotNull]=

Combining filters with OR

Several filter[...] parameters are combined with AND. To express alternatives, put conditions into numbered OR groups:

filter[or][<group>][field]=value
filter[or][<group>][field.operator]=value

Conditions inside one group are AND-ed, the groups are OR-ed with each other, and the whole disjunction is AND-ed with any plain filters outside it.

bash
# region = 'EU' AND ( (status='paid' AND total>=100) OR priority='urgent' )
curl ".../api/v1/acme-corp/sales/orders\
?filter[region]=EU\
&filter[or][0][status]=paid\
&filter[or][0][total.gte]=100\
&filter[or][1][priority]=urgent" \
  -H "Authorization: Bearer sk_live_abc123..."

Group numbers only separate the groups — they do not rank them, and they need not start at zero or be contiguous.

Malformed groups are refused

filter[or][field]=value (no group number) or a non-numeric group number returns a 400. Ignoring a malformed group would widen the result — you would be handed rows you meant to filter out — so it is refused instead.

Row-level security is never widened by OR

If the workspace uses row-level security, those conditions are applied outside your groups. customer_id = you AND (your alternatives) — an OR in your filter can never reach another user's rows.

Examples

Basic Equality Filter

bash
curl ".../api/v1/acme-corp/sales/customers?filter[name]=Alice" \
  -H "Authorization: Bearer sk_live_abc123..."

Numeric Range

bash
# Customers aged 18-65
curl ".../api/v1/acme-corp/sales/customers?filter[age.gte]=18&filter[age.lte]=65" \
  -H "Authorization: Bearer sk_live_abc123..."
bash
# Customers with "smith" in their name
curl ".../api/v1/acme-corp/sales/customers?filter[name.like]=smith" \
  -H "Authorization: Bearer sk_live_abc123..."

Multiple Values (IN)

bash
# Orders with status "pending" or "processing"
curl ".../api/v1/acme-corp/sales/orders?filter[status.in]=pending,processing" \
  -H "Authorization: Bearer sk_live_abc123..."

Null Checks

bash
# Customers without an email address
curl ".../api/v1/acme-corp/sales/customers?filter[email.isNull]=" \
  -H "Authorization: Bearer sk_live_abc123..."

Combining with Other Features

Filters work seamlessly with pagination, sorting, expansion, and field selection:

bash
# Active customers, sorted by name, page 2, with order count
curl ".../api/v1/acme-corp/sales/customers\
  ?filter[status]=active\
  &sort=name,asc\
  &page=1&size=10\
  &expand=orders\
  &fields=id,name,email,orders" \
  -H "Authorization: Bearer sk_live_abc123..."

Type Conversion

Filter values are automatically converted to match the column type:

Column TypeFilter ValueConverted To
INTEGER, BIGINT"42"42 (Long/Integer)
DECIMAL, NUMERIC"19.99"19.99 (BigDecimal)
BOOLEAN"true"true (Boolean)
VARCHAR, TEXT"hello""hello" (String)

A value that will not convert matches nothing

A value that cannot be converted to the column's type does not currently return an error. filter[age]=abc on an INTEGER column answers 200 with an empty result set, as though no row matched.

This is a known defect — it should be a 400 — and this page described it as one until it was checked against the running API. A filter that silently matches nothing is easy to mistake for a filter that legitimately found nothing, so validate values before you send them.

Unknown Fields

Filtering by a field that doesn't exist on the entity returns 400 BAD_REQUEST:

json
{
  "error": {
    "code": "BAD_REQUEST",
    "message": "Unknown filter field: 'nonexistent'"
  }
}

Filterable Fields Whitelist

Workspace admins can restrict which fields are filterable per entity via the API configuration. When a whitelist is set, only the listed fields accept filter queries. Attempting to filter on a non-whitelisted field returns 400 BAD_REQUEST.

SchemaStack Documentation