Skip to content

Pagination Guide

This API uses cursor-based pagination for list endpoints. This guide explains how to navigate through paginated results.

Response Structure

All paginated endpoints return responses in this format:

{
  "data": [...],
  "meta": {
    "next": "https://api.example.com/v1/enrolments/?limit=100&after=obj-123",
    "prev": "https://api.example.com/v1/enrolments/?limit=100&before=obj-100",
    "count": 250
  }
}
  • data: Array of items for the current page
  • meta.next: Full URL to fetch the next page (null if on last page)
  • meta.prev: Full URL to fetch the previous page (null if on first page)
  • meta.count: Total number of items matching your filters

Query Parameters

limit

Controls the number of items per page. Different pages may have different default and maximum values. Generally, the default value should be safe for most use cases, and not tax your rate limits too heavily.

Example:

GET /v1/enrolments/?limit=50

Initial Request

Start by making a request without any cursor parameters:

GET /v1/enrolments/?limit=100

Response:

{
  "data": [
    {"id": "enrol_001"},
    {"id": "enrol_002"},
    ...
    {"id": "enrol_100"}
  ],
  "meta": {
    "next": "https://api.example.com/v1/enrolments/?limit=100&after=enrol_100",
    "prev": null,
    "count": 250
  }
}

Moving Forward

To fetch the next page, use the URL provided in meta.next:

GET /v1/enrolments/?limit=100&after=enrol_100

Response:

{
  "data": [
    {"id": "enrol_101"},
    {"id": "enrol_102"},
    ...
    {"id": "enrol_200"}
  ],
  "meta": {
    "next": "https://api.example.com/v1/enrolments/?limit=100&after=enrol_200",
    "prev": "https://api.example.com/v1/enrolments/?limit=100&before=enrol_101",
    "count": 250
  }
}

Moving Backward

To fetch the previous page, use the URL provided in meta.prev:

GET /v1/enrolments/?limit=100&before=enrol_101

Reaching the End

When you reach the last page, meta.next will be null:

{
  "data": [
    {"id": "enrol_201"},
    {"id": "enrol_202"},
    ...
    {"id": "enrol_250"}
  ],
  "meta": {
    "next": null,
    "prev": "https://api.example.com/v1/enrolments/?limit=100&before=enrol_201",
    "count": 250
  }
}

Combining with Filters

Pagination works seamlessly with other query parameters. The next and prev URLs will preserve your filters:

GET /v1/enrolments/?start_datetime=2025-01-01T00:00:00Z&limit=50

Response:

{
  "data": [...],
  "meta": {
    "next": "https://api.example.com/v1/enrolments/?start_datetime=2025-01-01T00:00:00Z&limit=50&after=enrol_123",
    "prev": null,
    "count": 75
  }
}

Error Responses

Invalid Cursor

If you provide an after or before cursor that doesn't exist:

Status: 422 Unprocessable Entity

{
  "detail": [
    {
      "loc": ["query", "after"],
      "msg": "Invalid cursor"
    }
  ]
}

Limit Too Large

If you request a limit exceeding the maximum allowed:

Status: 422 Unprocessable Entity

{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "Limit must be less than or equal to 1000"
    }
  ]
}

Tips

  • Use the provided URLs: Always use meta.next and meta.prev URLs exactly as provided. Don't construct cursor values manually.
  • Check for null: Before following meta.next or meta.prev, check that they are not null.
  • Handle errors gracefully: Cursors can become invalid if the underlying data is deleted. Implement error handling for 422 responses.

Example

Here's a Python example that fetches all enrolments:

import httpx

def fetch_all_enrolments(api_key: str, base_url: str) -> list:
    items = []
    url = f"{base_url}/v1/enrolments/?limit=100"
    headers = {"Authorization": f"Bearer {api_key}"}
    with httpx.Client(headers=headers) as client:
        while url:
            response = client.get(url)
            response.raise_for_status()
            data = response.json()
            items.extend(data["data"])
            url = data["meta"]["next"]

    return items