Category: Query Parameters & Response Design
Description
Collection endpoints that return multiple items must support pagination using a consistent, well-established pagination style to ensure scalability and performance.
What It Checks
This rule validates that GET operations returning arrays (collections) implement pagination using one of four standard styles:
- Page Number Pagination: Uses
page(required) and optionallysizeorper_page - Limit/Offset Pagination: Uses
limit/page_size(required) andoffset/page_start_index(required) - Cursor-Based Pagination: Uses
cursor(required) - Time-Based Pagination: Uses
sinceanduntil(required), optionallylimit
The rule checks:
- GET endpoints that return arrays/collections
- Query parameters and headers for pagination support
- Consistency of pagination style across the entire API
- That all required parameters for a detected style are present
Why It Matters
Pagination is essential for scalable APIs and good user experience:
- Performance: Without pagination, large collections can cause timeouts, excessive memory usage, and slow response times
- Scalability: Pagination ensures your API can handle growing datasets without degrading performance
- User Experience: Clients can request manageable chunks of data and navigate through results efficiently
- Resource Management: Prevents overwhelming clients with massive payloads and reduces bandwidth consumption
- Consistency: Using a single pagination style across your API makes it predictable and easier to integrate
Examples
Good ✅
Page Number Pagination
paths:
/users:
get:
summary: List all users
parameters:
- name: page
in: query
required: true
schema:
type: integer
minimum: 1
- name: per_page
in: query
schema:
type: integer
minimum: 1
maximum: 100
default: 20
responses:
'200':
description: Paginated list of users
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/User'
page:
type: integer
total_pages:
type: integer
Limit/Offset Pagination
paths:
/products:
get:
summary: List products
parameters:
- name: limit
in: query
required: true
schema:
type: integer
minimum: 1
maximum: 100
- name: offset
in: query
required: true
schema:
type: integer
minimum: 0
responses:
'200':
description: Paginated products
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Product'
total:
type: integer
Cursor-Based Pagination
paths:
/posts:
get:
summary: List posts with cursor pagination
parameters:
- name: cursor
in: query
schema:
type: string
description: Opaque cursor for pagination
- name: limit
in: query
schema:
type: integer
default: 20
responses:
'200':
description: Paginated posts
content:
application/json:
schema:
type: object
properties:
data:
type: array
items:
$ref: '#/components/schemas/Post'
next_cursor:
type: string
nullable: true
prev_cursor:
type: string
nullable: true
Time-Based Pagination
paths:
/events:
get:
summary: List events within time range
parameters:
- name: since
in: query
required: true
schema:
type: string
format: date-time
- name: until
in: query
required: true
schema:
type: string
format: date-time
- name: limit
in: query
schema:
type: integer
default: 50
responses:
'200':
description: Time-filtered events
Bad ❌
No Pagination Support
paths:
/users:
get:
summary: List all users
# Missing pagination parameters entirely
responses:
'200':
description: All users
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/User'
Incomplete Pagination Parameters
paths:
/products:
get:
summary: List products
parameters:
- name: limit
in: query
schema:
type: integer
# Missing 'offset' parameter - incomplete limit/offset style
responses:
'200':
description: Products
Mixed Pagination Styles
paths:
/users:
get:
summary: List users
parameters:
- name: page
in: query
schema:
type: integer
- name: per_page
in: query
schema:
type: integer
# Page number style
/products:
get:
summary: List products
parameters:
- name: limit
in: query
schema:
type: integer
- name: offset
in: query
schema:
type: integer
# Limit/offset style - inconsistent with /users
Custom/Non-Standard Pagination
paths:
/orders:
get:
summary: List orders
parameters:
- name: start_index # Non-standard parameter name
in: query
schema:
type: integer
- name: count # Non-standard parameter name
in: query
schema:
type: integer
Configuration
This rule has no configuration options. It validates against the four standard pagination styles:
- Page Number:
page+ optionalsize/per_page - Limit/Offset:
limit/page_size+offset/page_start_index - Cursor:
cursor - Time-Based:
since+until+ optionallimit
The rule automatically normalizes parameter names (handles variations like pageNumber, page_number, etc.) and validates parameter types.
Common Violations
-
Missing pagination entirely: Collection endpoints that return all items without pagination support
-
Incomplete parameters: Using
limitwithoutoffset, orpagewithout making it clear how many items per page -
Inconsistent styles: Using page number pagination for some endpoints and cursor pagination for others
-
Non-standard names: Using custom parameter names like
start,count,pageNuminstead of standard names -
Wrong parameter types: Using string type for
pageorlimitinstead of integer
How to Fix
1. Choose a Pagination Style
Select one of the four standard styles based on your needs:
- Page Number: Best for simple use cases with stable data
- Limit/Offset: Good for SQL-backed APIs with stable data
- Cursor: Best for real-time feeds, infinite scroll, or frequently changing data
- Time-Based: Ideal for time-series data or event logs
2. Add Required Parameters
Ensure all required parameters for your chosen style are present:
# Page Number Style
parameters:
- name: page
in: query
required: true
schema:
type: integer
- name: per_page
in: query
schema:
type: integer
3. Use Standard Parameter Names
Stick to recognized names:
- Page number:
page,page_number - Page size:
size,per_page - Limit:
limit,page_size - Offset:
offset,page_start_index - Cursor:
cursor - Time range:
since,until
4. Ensure Consistency Across API
Use the same pagination style for all collection endpoints:
# All collection endpoints should use the same style
/users → page + per_page
/products → page + per_page
/orders → page + per_page
5. Update Response Format
Include pagination metadata in responses:
{
"data": [...],
"page": 2,
"per_page": 20,
"total": 150,
"total_pages": 8,
"has_next": true,
"has_previous": true
}
Or for cursor-based:
{
"data": [...],
"next_cursor": "eyJpZCI6MTIzfQ==",
"prev_cursor": "eyJpZCI6OTh9"
}
Related Rules
- Rule 1: Plural Collections - Ensures collection endpoints use plural nouns
- Rule 19: Query Parameters for Filtering - Proper use of query parameters
- Rule 52: Response Envelope Pattern - Structuring paginated responses