Version: 1.0.0
Category: Pattern
Status: Stable
Pagination patterns define how AI agents should handle paginated API responses when querying lists of entities. Structs APIs use key-based pagination for efficient data retrieval.
Key Principles:
Format: Uses pagination.key and pagination.limit parameters
Request:
{
"request": {
"method": "GET",
"url": "/structs/player",
"queryParams": {
"pagination.key": "",
"pagination.limit": 100
}
}
}
Response:
{
"response": {
"status": 200,
"body": {
"Player": [...],
"pagination": {
"next_key": "base64_encoded_key",
"total": "1000"
}
}
}
}
Next Page Request:
{
"request": {
"method": "GET",
"url": "/structs/player",
"queryParams": {
"pagination.key": "base64_encoded_key",
"pagination.limit": 100
}
}
}
Format: Uses offset and limit parameters
Request:
{
"request": {
"method": "GET",
"url": "/api/guild/directory",
"queryParams": {
"offset": 0,
"limit": 50
}
}
}
Response:
{
"response": {
"status": 200,
"body": {
"guilds": [...],
"pagination": {
"offset": 0,
"limit": 50,
"total": 200,
"hasMore": true
}
}
}
}
pagination.key (string, optional):
next_key from previous response for subsequent pagesnull or empty indicates last pagepagination.limit (integer, optional):
pagination.offset (integer, optional):
offset (integer, optional):
limit for next pagelimit (integer, optional):
Use Case: Need complete dataset
Pattern:
{
"strategy": "fetch-all",
"steps": [
{
"step": 1,
"action": "fetch-first-page",
"pagination": {
"key": "",
"limit": 100
}
},
{
"step": 2,
"action": "check-next-key",
"condition": "response.pagination.next_key !== null"
},
{
"step": 3,
"action": "fetch-next-page",
"pagination": {
"key": "",
"limit": 100
},
"repeat": "until next_key is null"
}
]
}
Implementation Example:
async function fetchAllPlayers() {
const allPlayers = [];
let nextKey = '';
do {
const response = await fetch(
`/structs/player?pagination.key=${nextKey}&pagination.limit=100`
);
const data = await response.json();
allPlayers.push(...data.Player);
nextKey = data.pagination?.next_key || '';
} while (nextKey);
return allPlayers;
}
Use Case: Need specific page of results
Pattern:
{
"strategy": "fetch-specific-page",
"page": 3,
"pageSize": 50,
"steps": [
{
"step": 1,
"action": "calculate-offset",
"offset": "(page - 1) * pageSize"
},
{
"step": 2,
"action": "fetch-page",
"pagination": {
"offset": 100,
"limit": 50
}
}
]
}
Use Case: Load data incrementally as needed
Pattern:
{
"strategy": "incremental-fetch",
"steps": [
{
"step": 1,
"action": "fetch-initial-page",
"pagination": {
"key": "",
"limit": 50
},
"store": "last_key"
},
{
"step": 2,
"action": "fetch-more-when-needed",
"pagination": {
"key": "",
"limit": 50
},
"trigger": "user-scrolls-or-requests-more"
}
]
}
Standard Format:
{
"Entity": [...],
"pagination": {
"next_key": "base64_key_or_null",
"total": "1000"
}
}
Fields:
Entity (array): Array of entity objectspagination.next_key (string |
null): Key for next page, null if last page |
pagination.total (string): Total number of items (may be approximate)Standard Format:
{
"data": [...],
"pagination": {
"offset": 0,
"limit": 50,
"total": 200,
"hasMore": true
}
}
Fields:
data (array): Array of data objectspagination.offset (integer): Current offsetpagination.limit (integer): Page sizepagination.total (integer): Total number of itemspagination.hasMore (boolean): Whether more pages existScenario: No items match query
Response:
{
"Player": [],
"pagination": {
"next_key": null,
"total": "0"
}
}
Handling:
next_key will be nullScenario: All results fit in one page
Response:
{
"Player": [...],
"pagination": {
"next_key": null,
"total": "25"
}
}
Handling:
next_key is nullScenario: Thousands of items
Best Practice:
Recommendation: 50-100 items per page
Why:
Pattern:
{
"cache": {
"lastKey": "base64_key",
"lastPage": 5,
"totalFetched": 500,
"timestamp": "2025-01-01T00:00:00Z"
}
}
Benefits:
Pattern:
{
"errorHandling": {
"networkError": "retry-current-page",
"rateLimit": "wait-and-retry",
"serverError": "skip-page-and-continue"
}
}
Best Practice:
Checks:
next_key format (base64 or null)total is non-negative{
"workflow": "fetch-all-players",
"steps": [
{
"step": 1,
"endpoint": "player-list",
"method": "GET",
"url": "/structs/player",
"parameters": {
"pagination.key": "",
"pagination.limit": 100
},
"extract": {
"players": "response.body.Player",
"nextKey": "response.body.pagination.next_key"
}
},
{
"step": 2,
"condition": "nextKey !== null",
"endpoint": "player-list",
"method": "GET",
"url": "/structs/player",
"parameters": {
"pagination.key": "",
"pagination.limit": 100
},
"repeat": "until nextKey is null"
}
],
"result": {
"allPlayers": ""
}
}
{
"workflow": "fetch-guild-directory",
"steps": [
{
"step": 1,
"endpoint": "webapp-guild-directory",
"method": "GET",
"url": "/api/guild/directory",
"parameters": {
"offset": 0,
"limit": 50
},
"extract": {
"guilds": "response.body.data",
"hasMore": "response.body.pagination.hasMore",
"total": "response.body.pagination.total"
}
},
{
"step": 2,
"condition": "hasMore === true",
"endpoint": "webapp-guild-directory",
"method": "GET",
"url": "/api/guild/directory",
"parameters": {
"offset": 50,
"limit": 50
},
"repeat": "until hasMore is false"
}
]
}
total may be approximatenext_key can be nullprotocols/query-protocol.mdapi/endpoints.mdprotocols/error-handling.mdapi/rate-limits.mdAPI Documentation Specialist - January 2025