Skip to main content

Cursor-based Pagination

The most common pagination type. The API returns a cursor/token pointing to the next page.

When to Use

  • API returns a cursor, next_cursor, starting_after, or similar token
  • API returns a has_more boolean
  • Response includes a link to the next page as a token value

Configuration

{
enabled: true,
type: "cursor",
paramsLocation: "query",
params: {
cursorParam: "cursor",
limitParam: "limit",
defaultLimit: 100
},
response: {
nextCursorPath: "meta.next_cursor",
hasMorePath: "meta.has_more",
dataArrayPath: "data"
},
stopCondition: {
type: "cursor_empty"
}
}

Example: Stripe API

API Behavior

Request:

GET /v1/customers?limit=100
GET /v1/customers?limit=100&starting_after=cus_abc123

Response:

{
"object": "list",
"data": [
{ "id": "cus_abc123", "email": "a@example.com" },
{ "id": "cus_def456", "email": "b@example.com" }
],
"has_more": true,
"url": "/v1/customers"
}

Configuration

{
type: "cursor",
params: {
cursorParam: "starting_after",
limitParam: "limit",
defaultLimit: 100
},
response: {
nextCursorPath: "data[-1].id",
hasMorePath: "has_more",
dataArrayPath: "data"
},
stopCondition: {
type: "boolean_flag",
flagPath: "has_more",
flagValue: false
}
}

Note: Stripe uses the last item's ID as the cursor, hence data[-1].id.

Example: Slack API

API Behavior

Request:

GET /users.list?limit=200
GET /users.list?limit=200&cursor=dXNlcjpVMDYxTkZUVDI=

Response:

{
"ok": true,
"members": [
{ "id": "U061NFTT2", "name": "alice" },
{ "id": "U061NFTT3", "name": "bob" }
],
"response_metadata": {
"next_cursor": "dXNlcjpVMDYxTkZUVDI="
}
}

Configuration

{
type: "cursor",
params: {
cursorParam: "cursor",
limitParam: "limit",
defaultLimit: 200
},
response: {
nextCursorPath: "response_metadata.next_cursor",
dataArrayPath: "members"
},
stopCondition: {
type: "cursor_empty"
}
}

Common Cursor Parameter Names

APIParameter
Stripestarting_after
Slackcursor
Twitterpagination_token
Facebookafter
Genericcursor, next

Stop Conditions for Cursor

Empty Cursor (Most Common)

Stop when cursor is null, empty string, or missing:

{
stopCondition: {
type: "cursor_empty"
}
}

Boolean Flag

Stop when a field equals a specific value:

{
stopCondition: {
type: "boolean_flag",
flagPath: "has_more",
flagValue: false
}
}

Troubleshooting

Cursor Not Changing

Symptoms: Same data retrieved repeatedly

Check:

  • Cursor path is correct
  • Cursor is being sent in requests
  • API is receiving cursor parameter

Pagination Stops Early

Symptoms: Missing data

Check:

  • Stop condition not triggering prematurely
  • Cursor path returns actual cursor value
  • has_more path is correct

Infinite Loop

Symptoms: Pagination never stops

Check:

  • Stop condition is configured
  • Cursor eventually becomes empty/null
  • Compare API docs for expected behavior