Category: Design Tags: pagination, sorting, filtering, field-selection, count, cursor, offset, query-string
limit query parameter specifying the maximum number of records per page.offset query parameter (number of records/pages to skip).sortby query string parameter.,) without spaces, in priority order.+ for ascending order.- for descending order. Omitting a prefix defaults to descending order.name=bob), not a generic filter= wrapper.&./status endpoint MUST return one of: Ready, Processing, or Error.404 until results are ready.select query string parameter.select parameter SHOULD NOT determine the order of fields in the response.count query string parameter (no value, no equals sign).Specifies a page size and a number of records to skip.
GET /items?limit=20&offset=3
This returns 20 records, skipping the first 60 (3 × 20) records.
Advantages:
Disadvantages:
The response for each page contains a cursor (typically the ID of the last record) used to fetch the next page.
GET /items?limit=20&after=1234
This returns 20 records starting after record 1234.
For bidirectional navigation:
GET /items?limit=20&before=5678
Response structure for cursor pagination:
{
"data": [
{ "id": "1235", ... },
{ "id": "1236", ... }
],
"paging": {
"cursors": {
"after": "1254",
"before": "1235"
}
}
}
after: cursor value representing the last record in the current page (use to get the next page).before: cursor value representing the first record in the current page (use to get the previous page).Rules:
Advantages:
Disadvantages:
GET /items?sortby=name
GET /items?sortby=name,age
GET /items?sortby=-name,+age
| Syntax | Meaning |
|---|---|
sortby=name |
Sort by name descending (default) |
sortby=+name |
Sort by name ascending |
sortby=-name |
Sort by name descending |
sortby=+name,+age |
Sort by name ascending, then by age ascending |
sortby=-name,+age |
Sort by name descending, then by age ascending |
Criteria are specified directly as query string parameters.
GET /items?name=bob&ordercount>0
Operator syntax:
| Operator | Description | Example |
|---|---|---|
= |
Equal | name=bob |
<> |
Not equal | name<>'jane jones' |
> |
Greater than | price>10.00 |
>= |
Greater than or equal | age>=18 |
< |
Less than | price<100.00 |
<= |
Less than or equal | age<=13 |
Placement: Filter parameters SHOULD appear at the end of the query string, after all other parameters (e.g. limit, sortby).
Advantages:
Disadvantages:
For complex or long-running filters, POST the criteria and GET the results separately.
POST /items/search
Content-Type: application/json
{ "filter": { "country": "DE", "status": "active", "tags": ["premium"] } }
→ 202 Accepted
Location: /items/search/results/a1b2c3
Check status:
GET /items/search/results/a1b2c3/status
→ 200 OK
{ "status": "Processing" } ← or "Ready" or "Error"
Retrieve results when ready:
GET /items/search/results/a1b2c3
→ 200 OK (when Ready)
{ "data": [...] }
→ 404 Not Found (when still Processing or not yet started)
Status values: Ready, Processing, Error.
Advantages:
Disadvantages:
POST the criteria and receive the filtered results immediately in the response.
POST /items/search
Content-Type: application/json
{ "filter": { "country": "DE", "status": "active" } }
→ 200 OK
{ "data": [...] }
Advantages:
Disadvantages:
flowchart TD
A["Choose a filtering approach"] --> SLA{"Response time\nSLA required?"}
SLA -- Yes --> AsyncFilter["POST filter → GET response\n(asynchronous)"]
SLA -- No --> Length{"Filter criteria\nexceeds ~2000 chars?"}
Length -- Yes --> PostFilter{"Need immediate\nresponse?"}
PostFilter -- Yes --> PostImmediate["POST filter → immediate response"]
PostFilter -- No --> AsyncFilter
Length -- No --> QS["Query string filtering"]
Allows clients to request only specific fields, reducing payload size.
GET /items?select=name
GET /items?select=name,age,email
The order of fields in the select parameter SHOULD NOT affect the order they appear in the response. Field ordering in the response is determined by the server.
Note: Each unique combination of selected fields must be cached separately — consider this when designing cache strategies.
Returns the total count of matching records without returning the records themselves.
GET /items?count
GET /items?name=bob&count
Response is always a single integer:
42
Rules:
count without = (it is a flag, not a key-value parameter).count MAY be combined with filtering to count matching records.count SHOULD NOT be combined with sortby, limit, offset, or select — these parameters will be ignored.Query parameters may be combined. The standard precedence is:
GET /items?limit=20&after=1234&sortby=+name&name=bob&select=id,name,age
This request:
name=bobname ascending1234id, name, and age fieldsFiltering parameters SHOULD appear after pagination, sorting, and field-selection parameters in the query string.