← Back to Platform Architecture
Pagination, Sorting, and Filtering
Overview
All Stratum Platform list endpoints support consistent pagination, sorting, and filtering parameters. This enables efficient querying of large datasets and client-side control over result ordering and scope.
Pagination Strategy
Phase 1: Offset-Limit Pagination
Current implementation uses offset/limit with total count. Simple, stateless, suitable for UI pagination.
GET /api/appeals?limit=20&offset=0
Phase 2: Cursor-Based Pagination (Roadmap)
Future implementation will use cursor-based pagination for better performance on large datasets:
GET /api/appeals?limit=20&after=cursor-abc123
Phase 1: Offset-Limit Pagination
Query Parameters
| Parameter | Type | Default | Max | Description |
limit | integer | 20 | 100 | Number of results per page |
offset | integer | 0 | unlimited | Number of results to skip (0-indexed) |
sort_by | string | created_at | - | Column to sort by |
sort_order | string | DESC | - | ASC or DESC |
Response Format
{
"data": [
{ "id": "appeal-1", "status": "submitted", ... },
{ "id": "appeal-2", "status": "draft", ... }
],
"pagination": {
"limit": 20,
"offset": 0,
"total": 156,
"has_more": true
}
}
Fields:
data — Array of results
pagination.limit — Requested limit
pagination.offset — Requested offset
pagination.total — Total matching records (calculated from database)
pagination.has_more — Boolean: true if more results exist beyond current page
Examples
Get first 20 appeals:
curl "https://stratum-platform-api.fly.dev/api/appeals?limit=20&offset=0" \
-H "X-User-Id: user-123"
Get next page (page 2):
curl "https://stratum-platform-api.fly.dev/api/appeals?limit=20&offset=20" \
-H "X-User-Id: user-123"
Get all precedents sorted by overturn_rate descending:
curl "https://stratum-platform-api.fly.dev/api/precedents?limit=50&offset=0&sort_by=overturn_rate&sort_order=DESC" \
-H "X-User-Id: user-123"
Sorting
Sortable Columns
Each endpoint defines sortable columns. Common columns:
| Endpoint | Sortable Columns |
/appeals | created_at, status, precedent_id, payer_id |
/precedents | created_at, overturn_rate, maturity_tier, denial_code |
/marketplace/clusters | clinical_relevance_score, precedent_count, min_overturn_rate |
/registries/{slug} | created_at, display_name, (registry-specific) |
Sort Order
ASC (Ascending) — Smallest to largest, A to Z, earliest to latest
DESC (Descending) — Largest to smallest, Z to A, latest to earliest
Default: DESC for timestamp columns, ASC for others.
Sort Examples
Oldest appeals first:
curl "https://stratum-platform-api.fly.dev/api/appeals?sort_by=created_at&sort_order=ASC" \
-H "X-User-Id: user-123"
Highest overturn rates first:
curl "https://stratum-platform-api.fly.dev/api/precedents?sort_by=overturn_rate&sort_order=DESC" \
-H "X-User-Id: user-123"
Most clinically relevant marketplace clusters:
curl "https://stratum-platform-api.fly.dev/api/marketplace/clusters?sort_by=clinical_relevance_score&sort_order=DESC" \
-H "X-User-Id: user-123"
Filtering
Filter Parameters
Filtering uses query string parameters matching column names:
GET /api/appeals?status=submitted&payer_id=cigna-commercial&state_code=CA
Common Filters
| Endpoint | Filter | Type | Example |
/appeals | status | string | status=submitted |
/appeals | payer_id | UUID | payer_id=payer-123 |
/appeals | state_code | string (2-char) | state_code=CA |
/precedents | maturity_tier | string | maturity_tier=mature |
/precedents | denial_code | string | denial_code=NOT_MEDICALLY_NECESSARY |
/precedents | payer_id | UUID | payer_id=cigna-commercial |
/precedents | visibility | string | visibility=public |
/registries/{slug} | is_active | boolean | is_active=true |
Filter Examples
Get all submitted appeals:
curl "https://stratum-platform-api.fly.dev/api/appeals?status=submitted" \
-H "X-User-Id: user-123"
Get all mature precedents for Cigna:
curl "https://stratum-platform-api.fly.dev/api/precedents?maturity_tier=mature&payer_id=cigna-commercial" \
-H "X-User-Id: user-123"
Get public precedents with high overturn rates in CA:
curl "https://stratum-platform-api.fly.dev/api/precedents?visibility=public&state_code=CA&sort_by=overturn_rate&sort_order=DESC" \
-H "X-User-Id: user-123"
Get active payer registry entries:
curl "https://stratum-platform-api.fly.dev/api/registries/payer-registry?is_active=true" \
-H "X-User-Id: user-123"
Multi-Value Filters
Some filters accept comma-separated values:
# Get appeals for multiple payers
curl "https://stratum-platform-api.fly.dev/api/appeals?payer_id=cigna-commercial,aetna-commercial,united-health" \
-H "X-User-Id: user-123"
Get precedents with multiple maturity tiers
curl "https://stratum-platform-api.fly.dev/api/precedents?maturity_tier=mature,canonical" \
-H "X-User-Id: user-123"
Combining Pagination, Sorting, and Filtering
All three can be combined in a single request:
Get page 2 of submitted appeals, sorted by most recent, for Cigna:
curl "https://stratum-platform-api.fly.dev/api/appeals?limit=20&offset=20&status=submitted&payer_id=cigna-commercial&sort_by=created_at&sort_order=DESC" \
-H "X-User-Id: user-123"
Response:
{
"data": [
{ "id": "app-001", "status": "submitted", "payer_id": "cigna-commercial", "created_at": "2026-04-13T15:30:00Z" },
{ "id": "app-002", "status": "submitted", "payer_id": "cigna-commercial", "created_at": "2026-04-12T10:15:00Z" }
],
"pagination": {
"limit": 20,
"offset": 20,
"total": 156,
"has_more": true
}
}
Performance Considerations
Pagination Best Practices
Always set limit: Don't rely on defaults; explicit limits improve predictability
Avoid large offsets: Offset pagination becomes slow with large offset values (>5000)
Use reasonable page size: 20-50 results is typical; avoid extremely large limits
Implement client-side caching: Cache results locally to reduce API calls
Database Backing
All paginated endpoints use database indexes to ensure performance:
CREATE INDEX idx_appeals_by_tenant_status ON appeals(tenant_id, status);
CREATE INDEX idx_appeals_by_created_date_desc ON appeals(created_at DESC);
Filtering on non-indexed columns may be slow; report performance issues with endpoint name.
Cursor Pagination Roadmap
Future Phase 2 implementation will use opaque cursors instead of offsets:
# Phase 2 syntax (not yet available)
curl "https://stratum-platform-api.fly.dev/api/appeals?limit=20&after=cursor-abc123" \
-H "X-User-Id: user-123"
Benefits of cursor pagination:
Handles real-time data changes gracefully
Constant performance regardless of result set size
Prevents "skipped" results if data changes between pages
Edge Cases
Empty Result Set
{
"data": [],
"pagination": {
"limit": 20,
"offset": 0,
"total": 0,
"has_more": false
}
}
Offset Beyond Total
If offset >= total, returns empty data array but includes accurate pagination metadata:
curl "https://stratum-platform-api.fly.dev/api/appeals?limit=20&offset=999" \
-H "X-User-Id: user-123"
Response
{
"data": [],
"pagination": {
"limit": 20,
"offset": 999,
"total": 156,
"has_more": false
}
}
Invalid Sort Column
If sort_by column doesn't exist or isn't sortable, endpoint returns 400 Bad Request:
{
"status": 400,
"error": {
"type": "INVALID_SORT_COLUMN",
"message": "Column 'nonexistent_field' is not sortable on this endpoint",
"details": {
"sortable_columns": ["created_at", "status", "payer_id"]
}
}
}
Invalid Filter Value
If filter value doesn't match expected type/enum, returns 400 Bad Request:
{
"status": 400,
"error": {
"type": "INVALID_FILTER_VALUE",
"message": "Invalid value for status filter",
"details": {
"filter": "status",
"value": "invalid_status",
"valid_values": ["draft", "submitted", "under_review", "approved", "denied", "withdrawn", "archived"]
}
}
}
See Also
OVERVIEW.md — API design philosophy and response envelope
ENDPOINTS-APPEALS.md — Appeal endpoint filtering examples
ENDPOINTS-PRECEDENTS.md — Precedent endpoint filtering examples
ENDPOINTS-REGISTRIES.md — Registry endpoint filtering examples
07-GOVERNANCE/API-TROUBLESHOOTING.md — Pagination troubleshooting