Part 5 · 2 chapters · ~12 min

Errors, Pagination and Filtering

One error format with RFC 9457 problem details, validation errors per field, cursor pagination over keyset queries, filtering, sorting and sparse fieldsets, consistent list envelopes, and limits on page sizes.

11

Errors

code
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
{ "type": "https://api.bank.example/errors/insufficient-funds", "title": "Insufficient funds", "status": 422,
  "detail": "Available balance is ₦4,200.00; this transfer needs ₦5,025.00.",
  "code": "insufficient_funds", "request_id": "req_91af", "retryable": false }
ONE ERROR FORMAT: RFC 9457 PROBLEM DETAILS
machine-readable, human-readable, the same everywhere
typehttps://api.bank.example/errors/insufficient-funds (stable, documented)titleInsufficient fundsstatus422detailAvailable balance is ₦4,200.00; this transfer needs ₦5,025.00extensionscode, request_id, errors[] per field, retryable
swipe the figure sideways, or tap expand for full screen
1/4
stable type
The type URI (or a code) is the stable, documented identifier clients branch on. Titles and details can change wording; types never do.
clients branch on a stable type or codenever on the message text
12

Pagination, filtering and sorting

code
GET /v1/transfers?account_id=ac_1&state=completed&created_after=2026-09-01T00:00:00Z&limit=50
200 { "data": [ ... 50 items ... ], "next_cursor": "eyJ0IjoiMjAyNi0wOS0yOVQxMDowMDowMFoiLCJpZCI6InRyXzkxIn0", "has_more": true }

-- the cursor encodes the last (created_at, id); the query is keyset, so deep pages stay fast
SELECT * FROM transfers WHERE account_id = $1 AND (created_at, id) < ($2, $3) ORDER BY created_at DESC, id DESC LIMIT 51;
rulewhy
opaque cursors, not page numbersstable under inserts; keyset queries; free to change internally
a maximum limit (e.g. 100)protects the database
sort only by indexed, documented fieldsarbitrary sorts become full scans
filters as explicit query parametersa generic query language invites expensive queries
sparse fieldsets (?fields=id,state) if payloads are largebandwidth for mobile clients