Filtering and Searching
Filtering narrows a collection response using query parameters. Search adds full-text or structured search. Use consistent query parameter naming across all endpoints: filter[field]=value or ?status=active&role=admin. Never use POST for reads, even for complex filters.
Details
Filtering reduces the collection to matching items. Searching finds items by keyword or structured query. Both use GET with query parameters.
Filter parameter styles: Simple key=value: GET /users?status=active&role=admin Bracket notation: GET /users?filter[status]=active&filter[role]=admin Operator suffix: GET /products?price_gte=10&price_lte=100 JSON filter (complex): GET /users?filter={"age":{"gte":18}} (URL-encoded)
Pick one style and use it everywhere. Simple key=value is the most widely adopted.
Full-text search: use a dedicated query parameter: GET /products?q=bluetooth+headphones GET /users?search=alice
Sorting: use sort parameter with +/- prefix or field:asc/desc: GET /users?sort=-created_at (descending) GET /users?sort=name:asc,created_at:desc
Never use POST for filtering. Some teams use POST /users/search with a JSON body for complex filters, but this breaks caching, REST semantics, and client expectations. If the filter is too complex for a GET, expose a dedicated search endpoint at GET /search or use GraphQL.
Date range filtering: GET /orders?created_after=2026-01-01&created_before=2026-12-31 Use ISO 8601 format for all date parameters.
URL Examples
| Pattern | Description |
|---|---|
| GET /users?status=active&role=admin | Filter by multiple fields |
| GET /products?price_gte=10&price_lte=100 | Range filter |
| GET /users?q=alice | Full-text search |
| GET /orders?sort=-created_at&limit=20 | Sort descending, limit |
Do
- +Use GET for all filtering – never POST for reads
- +Pick one filter parameter style and apply it consistently across all endpoints
- +Use ISO 8601 for date filters: created_after=2026-01-01T00:00:00Z
- +Return total count in response metadata for filtered results
- +Document all available filter fields in OpenAPI spec
Don't
- !Never use POST /users/search for filtering – use GET /users?q=term
- !Never silently ignore unknown filter parameters – return 400 with error detail
- !Never filter server-side on fields the client cannot know about (security: server should filter by authenticated user's org automatically)
Examples
GET /api/v1/orders?status=shipped&created_after=2026-01-01&sort=-amount&page=1&limit=50
{
"data": [...],
"pagination": { "page": 1, "limit": 50, "total": 234 },
"filters": { "status": "shipped", "created_after": "2026-01-01" }
}