An API where /api/users uses cursor-based pagination and /api/orders uses offset/limit forces every consumer to write different pagination logic per endpoint. Worse, an endpoint with no pagination at all returns unbounded result sets that will eventually OOM the server or time out for large collections. iso-25010:2011 compatibility.interoperability is directly impacted: a generic list component cannot be built once and reused, and any SDK wrapping the API must special-case every list endpoint.
High because inconsistent pagination patterns prevent generic client-side list handling and unbounded collections create reliability failures at scale.
Define one pagination response shape as a generic type and apply it to every list endpoint:
interface PaginatedResponse<T> {
data: T[]
meta: {
page: number
limit: number
total: number
totalPages: number
}
}
// Use identically across all list endpoints:
// GET /api/users?page=1&limit=20
// GET /api/orders?page=2&limit=10
// Same envelope, same parameter names
Enforce a maximum page size (100 items) server-side to prevent consumers from accidentally requesting unbounded results. See the query-param-naming check for consistent parameter naming across these endpoints.
ID: api-design.contract-quality.pagination-contract
Severity: high
What to look for: Examine all list endpoints. Check whether a consistent pagination pattern is defined and used across all of them. Look for: offset-based (page/limit with total), cursor-based (cursor/limit with nextCursor), or keyset pagination. Check that the pagination metadata shape is consistent: { data: [...], meta: { page, limit, total } } or { data: [...], pageInfo: { hasNextPage, endCursor } }. Compare at least 2 list endpoints to verify they use the same pattern.
Pass criteria: List all endpoints that return collections. All list endpoints use the same pagination pattern (same parameter names, same response metadata shape). The pagination approach is appropriate for the data (cursor-based for real-time feeds, offset-based for static lists). A maximum page size is enforced with no more than 100 items per page default.
Fail criteria: List endpoints use different pagination patterns (one uses offset/limit, another uses cursor-based, another has no pagination). Or pagination metadata shapes differ across endpoints.
Skip (N/A) when: Fewer than 2 list endpoints exist, or all collections are known to be small (under 50 items by design).
Detail on fail: Note the inconsistency (e.g., "GET /api/users uses cursor-based pagination with nextCursor, but GET /api/orders uses page/limit with total count, and GET /api/comments has no pagination at all."). Max 500 chars.
Cross-reference: For query parameter naming consistency across these list endpoints, see the query-param-naming check in the Naming Conventions category above.
Remediation: Define one pagination pattern and apply it to all list endpoints:
// Standard pagination response shape:
interface PaginatedResponse<T> {
data: T[]
meta: {
page: number
limit: number
total: number
totalPages: number
}
}
// Use everywhere:
// GET /api/users?page=1&limit=20
// GET /api/orders?page=2&limit=10
// Same shape, same parameter names