Pagination, Sorting, and Filtering

← 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

Response Format

{
  "data": [
    { "id": "appeal-1", "status": "submitted", ... },
    { "id": "appeal-2", "status": "draft", ... }
  ],
  "pagination": {
    "limit": 20,
    "offset": 0,
    "total": 156,
    "has_more": true
  }
}
Fields:

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

    ParameterTypeDefaultMaxDescription
    limitinteger20100Number of results per page
    offsetinteger0unlimitedNumber of results to skip (0-indexed)
    sort_bystringcreated_at-Column to sort by
    sort_orderstringDESC-ASC or DESC
    EndpointSortable Columns
    /appealscreated_at, status, precedent_id, payer_id
    /precedentscreated_at, overturn_rate, maturity_tier, denial_code
    /marketplace/clustersclinical_relevance_score, precedent_count, min_overturn_rate
    /registries/{slug}created_at, display_name, (registry-specific)
    EndpointFilterTypeExample
    /appealsstatusstringstatus=submitted
    /appealspayer_idUUIDpayer_id=payer-123
    /appealsstate_codestring (2-char)state_code=CA
    /precedentsmaturity_tierstringmaturity_tier=mature
    /precedentsdenial_codestringdenial_code=NOT_MEDICALLY_NECESSARY
    /precedentspayer_idUUIDpayer_id=cigna-commercial
    /precedentsvisibilitystringvisibility=public
    /registries/{slug}is_activebooleanis_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