Octri

Pagination

A list endpoint that returns one page is a list endpoint every caller writes a loop around. Octri generates that loop for them: callers get each item in turn, and pages are fetched only as they're needed.

Often you don't have to configure anything. Octri recognizes common pagination shapes in your spec, and you can state the shape yourself when it doesn't.

What callers write

These are from generated SDKs. Each one yields items, not pages, and stops when the API signals the end. Nothing is prefetched, so stopping early saves requests.

typescript
for await (const user of client.users.list({ limit: 100 })) {
  console.log(user.id);
}

The operation itself is a lazy AsyncGenerator.

Single pages and whole pages

The plain method still exists in most languages, for a caller who wants one page. client.users.list() in Python returns one UserPage, with its next_cursor.

Cursor-paginated operations can also generate a companion that yields a page at a time, for callers who need page-level data such as a count. It's named for the operation: listUsersPages in TypeScript, Kotlin, PHP, Dart and Swift, and list_users_pages in Python and Rust.

How Octri finds pagination

The generator checks an operation in this order. The first match wins.

OrderStyleDetected from
FirstNext URLA next or next_url field holding a full URL
SecondCursorA cursor parameter and a matching response field
ThirdPage numberA page parameter
FourthOffsetAn offset parameter and a next_offset response field

Every style also needs an array of results in the response. Octri looks for data, items, results, records, entries, values or objects, in that order, then the first array it finds.

These names are recognized, ignoring case:

RoleNames
Cursor parametercursor, after, next_cursor, page_token, starting_after, start_cursor, continuation_token
Cursor in the responsenext_cursor, nextCursor, cursor, next_page_token, nextPageToken, next, after, last_id, lastId
More to comehas_more, hasMore, has_next, hasNext, more, is_truncated, isTruncated
Page parameterpage, page_number, pageNumber
Next page in the responsenext_page, nextPage
Total pagestotal_pages, totalPages, page_count, pageCount, last_page, lastPage, pages
Offset parameteroffset, skip, start
Next offset in the responsenext_offset, nextOffset
Offset needs next_offset to be detected

An offset parameter with only a total in the response is not detected, because that alone doesn't reliably say where to stop. Add next_offset, or state the pagination yourself. Check your SDK Studio preview to see what was found.

Setting it in SDK Studio

Select an endpoint and open its pagination settings. Each is saved on that endpoint, under pagination.

Enable paginationon or offdefault: detected

Generates paginated access for this endpoint. Off also stops it being detected.

pagination: the settings below, or false

Strategychoicedefault: detected

How the next page is asked for: cursor, offset or page.

pagination.strategy

Items fieldresponse fielddefault: detected

The response field holding the array of items, such as data.

pagination.itemsField

Request cursor parameterquery parameterdefault: detected

The query parameter sent with the next-page cursor, such as after. For the other strategies this is Offset parameter or Page parameter.

pagination.nextField

Response cursor fieldresponse pathdefault: the request parameter's name

Where the next cursor is in the response, such as meta.next, when it isn't named like the request parameter. Cursor strategy only.

pagination.responseField

Auto-paginate helperon or offdefault: on

Generates the iterator. Off leaves the method returning one page.

pagination.autoPaginate

Pagination stylechoice per languagedefault: generator

What the helper returns. Only TypeScript's output changes.

ValueThe method returns
generatorA lazy AsyncGenerator that yields items
paginatorA Paginator, with explicit page navigation

byLang.<language>.paginationStyle

Octri checks these against the operation's real parameters and response when it builds. If a name doesn't match, Octri ignores your settings for that endpoint and falls back to detection, rather than generating code that can't work. So if the result isn't what you set, check the names first: Items field and the response field must exist in the response schema, and the parameter must be a query parameter on the operation.

Turning pagination off for an endpoint also stops it being detected.

Setting it in your spec

Put an x-pagination extension on the operation. It wins over detection, and it can express more than the settings above.

yaml
paths:
  /users:
    get:
      operationId: listUsers
      x-pagination:
        type: cursor
        results: $response.data
        cursor:
          request: $request.after
          response: $response.last_id
          hasMore: $response.has_more

results is required. Then add the block for your type:

typeFieldMeaning
cursorrequestThe input parameter carrying the cursor
responseThe response field holding the next cursor. $response.data[-1].id uses the last item's ID
hasMoreOptional. Stops when false
offsetrequestThe offset parameter
limitOptional. The page-size parameter
responseOptional. The next-offset field
totalOptional. Stops at this total
pagerequestThe page-number parameter
responseOptional. The next-page field
totalPagesOptional. Stops at this page
urlnextThe field holding the next page's full URL
previousOptional. The previous page's URL

Set x-pagination: false to opt one operation out of detection.

Choosing a style

StyleGood forWatch out for
CursorMost APIs. Results stay stable while data changesCallers can't jump to a page
Next URLAPIs that already return linksSet it with x-pagination or rely on detection. SDK Studio's settings don't offer it
Page numberSmall, slowly changing listsRows inserted mid-read shift the window, so items repeat or get skipped
OffsetReporting over stable dataThe same shifting problem, and deep offsets are often slow

Troubleshooting