Pagination

Listings that can grow without bound come back a page at a time. Which page you get is decided by an opaque pagination token, and the SDK offers two ways to work with it: a paginator that follows the token for you, or a single-page call that hands you the token to drive yourself.

The shapes

Every paged listing in the SDK is one of these.

Shape

Returns

You do

Auto-paginating

Paginator[T] / AsyncPaginator[T]

Iterate. Pages are fetched lazily as you cross them.

Single page

A response object with a .pagination envelope

Read the page, then pass .pagination.next back as pagination_token.

Unpaged

A plain list-like model with no token

Iterate once. There is nothing to follow.

Several listings offer both of the first two, under names that differ by namespace (iter_*, *_page, or *_paginated on the index):

Listing

Auto-paginating

Single page

Indexes

pc.indexes.list()

—

Backups of one index

pc.indexes.list_backups(name)

—

Backups, project-wide

—

pc.backups.list()

Restore jobs

—

pc.restore_jobs.list()

Backup schedules

pc.backup_schedules.iter_schedules(index_name=…)

pc.backup_schedules.list(index_name=…)

Backup schedule history

pc.backup_schedules.iter_history(schedule_id=…)

pc.backup_schedules.history(schedule_id=…)

Assistants

pc.assistants.list()

pc.assistants.list_page()

Assistant files

pc.assistants.list_files(…)

pc.assistants.list_files_page(…)

Assistant operations

pc.assistants.list_operations(…)

pc.assistants.list_operations_page(…)

Documents

idx.documents.list(namespace=…)

—

Vector IDs

idx.list()

idx.list_paginated()

Namespaces

idx.list_namespaces()

idx.list_namespaces_paginated()

Bulk imports

idx.list_imports()

idx.list_imports_paginated()

Admin users, invites, service accounts, role bindings

admin.<ns>.list()

—

Unpaged listings (pc.collections.list(), pc.inference.list_models(), admin.api_keys.list(), admin.organizations.list(), admin.projects.list()) return every row in one response and carry no pagination attribute to check.

Reach for the single-page form when you need the token itself: to checkpoint a long walk to durable storage, to hand the next page to a different worker, or to resume after a crash. Otherwise iterate the paginator.

One paged surface is not a listing at all: idx.documents.fetch(filter=...) is a read whose filter can match unboundedly many documents, so it pages too. It takes a token and an optional limit page size; see When the first call takes no token.

Paginator and AsyncPaginator

Paginator and AsyncPaginator are lazy: no request is made until you start iterating, and each page is fetched only when iteration reaches it. They share one interface.

Member

What it gives you

__iter__ / __aiter__

Individual items, pages hidden

.pages()

Page objects, one per request

.to_list()

Every item, in one list

.pagination_token

Where iteration got to; None before the first fetch (without a resume token) and once the last page has been fetched

A paginator is not a list and not a snapshot. It has no len(), and iterating it twice sends the requests twice.

Iterating items

The common case. The loop crosses page boundaries without you noticing.

from pinecone import Pinecone

pc = Pinecone()

for backup in pc.indexes.list_backups("product-search"):
    print(backup.backup_id, backup.status)

Iterating pages

Use .pages() when you want the token each page carries, or when a page is the natural unit of work, such as one batch write or one checkpoint per page. The generator stops on its own when a page arrives without a token; you do not need to break on has_more.

from pinecone import Pinecone

pc = Pinecone()

for page in pc.backup_schedules.iter_history(schedule_id="sched-abc123").pages():
    for run in page.items:
        print(run.backup_id, run.status)
    print("next page:", page.pagination_token)  # None on the last page

Each Page carries:

Attribute

Type

Meaning

items

list[T]

The rows on this page, in server order

pagination_token

str | None

The page after this one; None on the last page

has_more

bool

pagination_token is not None

Collecting everything

to_list() walks every page and returns one list. Only reach for it when you know the listing is small: it holds the whole result in memory and cannot start work before the last page arrives.

from pinecone import Pinecone

pc = Pinecone()

backups = pc.indexes.list_backups("product-search").to_list()
print(len(backups))

Resuming

Page.pagination_token and Paginator.pagination_token are both accepted back as the pagination_token argument of the same list method. Store one, and a later process picks up where the earlier one stopped.

from pinecone import Pinecone

pc = Pinecone()

pages = pc.backup_schedules.iter_history(schedule_id="sched-abc123").pages()
first = next(pages)
for run in first.items:
    print(run.backup_id)

token = first.pagination_token  # save this somewhere durable

# Later, in another process:
for run in pc.backup_schedules.iter_history(
    schedule_id="sched-abc123", pagination_token=token
):
    print(run.backup_id)

Tokens are opaque. They are not IDs, offsets, or timestamps; do not parse one, build one, or edit one.

Paging walks a live result set rather than a fixed snapshot, so rows created or deleted between requests can shift later pages. De-duplicate on the row’s own identifier rather than relying on page order.

What limit means

limit is the page size the SDK asks the server for. On some paginators it also caps the total number of items iteration will yield:

  • Page size and total cap: pc.indexes.list, pc.indexes.list_backups, pc.backup_schedules.iter_schedules, pc.backup_schedules.iter_history, pc.assistants.list, pc.assistants.list_files, pc.assistants.list_operations.

  • Page size only (the paginator still walks every page): idx.documents.list, admin.users.list, admin.invites.list, admin.service_accounts.list, admin.role_bindings.list, and the Index listings (idx.list, idx.list_namespaces, idx.list_imports).

Check the method’s own limit documentation before relying on it to stop iteration. To stop early on any listing, break out of the loop or wrap it in itertools.islice(), which works on every listing.

# Stops after 50 items on any listing, regardless of limit semantics.
import itertools

from pinecone import Pinecone

pc = Pinecone()

for backup in itertools.islice(pc.indexes.list_backups("product-search"), 50):
    print(backup.backup_id)

When a limit truncates a page, that page reports pagination_token=None even though the server had more. Paginator.pagination_token still holds the server’s token for the following page, so resuming from it skips the rows the limit cut from the truncated page.

Driving the token yourself

Single-page methods return the page plus a pagination envelope. pagination is None, or pagination.next is None, both mean the page you are holding is the last one. That is the loop’s exit condition, not an error.

The loop below consumes each page before asking for the next one:

from pinecone import Pinecone

pc = Pinecone()

page = pc.backups.list(limit=100)
backups = list(page)

while page.pagination is not None and page.pagination.next is not None:
    page = pc.backups.list(pagination_token=page.pagination.next)
    backups.extend(page)

print(len(backups))

Pass limit on the first request only. The token already carries the page size it was minted with, so these backup listings omit limit whenever you pass pagination_token.

The async form is the same loop with await on each request:

from pinecone import AsyncPinecone

async with AsyncPinecone() as pc:
    page = await pc.backups.list(limit=100)
    backups = list(page)

    while page.pagination is not None and page.pagination.next is not None:
        page = await pc.backups.list(pagination_token=page.pagination.next)
        backups.extend(page)

    print(len(backups))

When the first call takes no token

Not every paged surface is a listing. idx.documents.fetch(filter=...) is a paged read: the filter can match an unbounded number of documents, so the response carries a pagination envelope, but there is no first-page call that differs from the rest. Write it as a while True with the token starting at None, and read each page’s documents before asking for the next one:

from pinecone import Pinecone

idx = Pinecone().index(name="articles-en")

pagination_token = None
while True:
    response = idx.documents.fetch(
        namespace="published",
        filter={"category": {"$eq": "tech"}},
        pagination_token=pagination_token,
    )
    for doc_id, doc in response.documents.items():
        print(doc_id, doc.title)
    if response.pagination is None:
        break
    pagination_token = response.pagination.next

Fetching by ids= instead of filter= is not paged: you get every requested ID in one response. Only the filtered form pages, and pagination_token is rejected without a filter.

Listings on the index

The data-plane listings on Index keep their own shape. The auto-following form is a plain generator, not a paginator, so .pages(), .to_list(), and .pagination_token are not available on it.

idx.list() and idx.list_namespaces() yield one response object per page, not one row at a time, so the loop body iterates the page as well:

from pinecone import Pinecone

pc = Pinecone()
idx = pc.index(name="product-search")

for page in idx.list(prefix="product#", limit=100):
    for item in page.vectors:
        print(item.id)

for page in idx.list_namespaces(prefix="prod-"):
    for ns in page.namespaces:
        print(ns.name, ns.record_count, ns.size_bytes)

idx.list_imports() is the exception: it yields the import records themselves.

from pinecone import Pinecone

idx = Pinecone().index(name="product-search")

for imp in idx.list_imports():
    print(imp.id, imp.status)

Each has a *_paginated twin that fetches exactly one page and leaves the token to you:

from pinecone import Pinecone

idx = Pinecone().index(name="product-search")

page = idx.list_paginated(prefix="product#", limit=100)
ids = [item.id for item in page.vectors]

while page.pagination is not None and page.pagination.next is not None:
    page = idx.list_paginated(
        prefix="product#",
        pagination_token=page.pagination.next,
    )
    ids.extend(item.id for item in page.vectors)

print(len(ids))

To inspect many namespaces, prefer idx.list_namespaces() over repeated describe_namespace() calls: it describes every namespace in one request per page.

See also