Pagination Patterns

Version: 1.0.0
Category: Pattern
Status: Stable


Overview

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:

  1. Use pagination keys to navigate through large datasets
  2. Respect page size limits (typically max 100 items per page)
  3. Handle empty results gracefully
  4. Continue until next_key is null to fetch all data
  5. Cache pagination state for resumable operations

Pagination Methods

1. Key-Based Pagination (Consensus Network)

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
    }
  }
}

2. Offset-Based Pagination (Some Webapp Endpoints)

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 Parameters

Consensus Network Parameters

pagination.key (string, optional):

pagination.limit (integer, optional):

pagination.offset (integer, optional):

Web Application Parameters

offset (integer, optional):

limit (integer, optional):


Pagination Strategies

Strategy 1: Fetch All Items

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;
}

Strategy 2: Fetch Specific Page

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
      }
    }
  ]
}

Strategy 3: Incremental Fetching

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"
    }
  ]
}

Pagination Response Formats

Consensus Network Format

Standard Format:

{
  "Entity": [...],
  "pagination": {
    "next_key": "base64_key_or_null",
    "total": "1000"
  }
}

Fields:

Web Application Format

Standard Format:

{
  "data": [...],
  "pagination": {
    "offset": 0,
    "limit": 50,
    "total": 200,
    "hasMore": true
  }
}

Fields:


Handling Edge Cases

Empty Results

Scenario: No items match query

Response:

{
  "Player": [],
  "pagination": {
    "next_key": null,
    "total": "0"
  }
}

Handling:

Single Page Results

Scenario: All results fit in one page

Response:

{
  "Player": [...],
  "pagination": {
    "next_key": null,
    "total": "25"
  }
}

Handling:

Large Datasets

Scenario: Thousands of items

Best Practice:


Best Practices

1. Use Appropriate Page Size

Recommendation: 50-100 items per page

Why:

2. Cache Pagination State

Pattern:

{
  "cache": {
    "lastKey": "base64_key",
    "lastPage": 5,
    "totalFetched": 500,
    "timestamp": "2025-01-01T00:00:00Z"
  }
}

Benefits:

3. Handle Errors Gracefully

Pattern:

{
  "errorHandling": {
    "networkError": "retry-current-page",
    "rateLimit": "wait-and-retry",
    "serverError": "skip-page-and-continue"
  }
}

4. Monitor Rate Limits

Best Practice:

5. Validate Pagination Data

Checks:


Pagination Examples

Example 1: Fetch All Players

{
  "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": ""
  }
}

Example 2: Fetch Guild Directory with Offset

{
  "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"
    }
  ]
}

Performance Considerations

Optimizing Pagination

  1. Use appropriate page sizes: 50-100 items typically optimal
  2. Cache responses: Avoid refetching same pages
  3. Parallel fetching: Fetch multiple pages in parallel (if supported)
  4. Filter early: Use query filters to reduce dataset size
  5. Monitor rate limits: Space out requests appropriately

Avoiding Common Pitfalls

  1. Don’t fetch all data unnecessarily: Only fetch what you need
  2. Don’t ignore rate limits: Respect API rate limits
  3. Don’t assume total is exact: total may be approximate
  4. Don’t skip error handling: Handle network and server errors
  5. Don’t forget to check for null: next_key can be null


Version History


API Documentation Specialist - January 2025