List endpoints that return multiple items support offset-based pagination via query parameters.
Query Parameters
| Parameter | Type | Default | Description |
|---|
skip | integer | 0 | Number of records to skip (offset) |
limit | integer | 20 | Number of items to return per page (1–100) |
Example Request
GET /v1/users?skip=20&limit=10
Response Shape
{
"success": true,
"data": [
{ "id": "...", "name": "Alice", "email": "alice@example.edu" },
{ "id": "...", "name": "Bob", "email": "bob@example.edu" }
],
"total": 53,
"page": 3,
"page_size": 10,
"total_pages": 6,
"message": null
}
| Field | Type | Description |
|---|
data | array | Items for the current page |
total | integer | Total items across all pages |
page | integer | Computed current page number (1-indexed) |
page_size | integer | Items per page (equals limit) |
total_pages | integer | ceil(total / limit) |
You send skip and limit as query params, and the response includes computed page, page_size, and total_pages for convenience.
Edge Cases
| Scenario | Behavior |
|---|
skip exceeds total | Returns data: [] with correct total and total_pages |
limit of 0 or negative | Rejected with VALIDATION_ERROR |
| No items match | Returns data: [], total: 0, total_pages: 0 |
Frontend Example
interface PaginatedResponse<T> {
success: true;
data: T[];
total: number;
page: number;
page_size: number;
total_pages: number;
}
async function fetchPage<T>(
endpoint: string,
skip: number = 0,
limit: number = 20
): Promise<PaginatedResponse<T>> {
const res = await apiCall(
`${endpoint}?skip=${skip}&limit=${limit}`
);
return res;
}
// Usage
const users = await fetchPage<User>("/v1/users", 0, 20);
// Next page
const nextPage = await fetchPage<User>("/v1/users", 20, 20);
// Check if there are more pages
const hasNext = users.page < users.total_pages;
const hasPrev = users.page > 1;