For AI agents: a documentation index is available at the root level at /llms.txt and /llms-full.txt. Append /llms.txt to any URL for a page-level index, or .md for the markdown version of any page.
my.pivotal.appGet an API key
Get StartedGuidesAPI ReferenceSDKsChangelog
Get StartedGuidesAPI ReferenceSDKsChangelog
  • Welcome
    • Introduction
    • Quickstart
    • Authentication
  • Concepts
    • Rate limits
    • Errors
    • Pagination
    • Versioning
    • Idempotency
LogoLogo
my.pivotal.appGet an API key
On this page
  • Parameters
  • Walking a list end to end
  • Filters and pagination interact
  • What we deliberately don’t do
  • Edge cases
Concepts

Pagination

Cursor-based, by created_at, descending.

|View as Markdown|Open in Claude|
Was this page helpful?
Edit this page
Previous

Errors

Next

Versioning

Built with

Every list endpoint in the Pivotal API uses cursor pagination. The pattern is the same shape across customers, contacts, and onboardings:

1GET /api/v1/customers?limit=20&cursor=2026-05-20T14:08:11.000Z

Response:

1{
2 "object": "list",
3 "data": [ /* 0–20 items */ ],
4 "has_more": true,
5 "next_cursor": "2026-05-19T22:41:03.000Z"
6}

Pass response.next_cursor straight back as the next request’s cursor. Stop when has_more is false.

Parameters

ParamTypeDefaultMaxNotes
limitinteger20100Page size. Larger = fewer round trips, more work per response.
cursorISO 8601 datetimenone—Returns items strictly older than this created_at.

cursor must validate as an ISO 8601 datetime. Garbage in returns a 400:

1{
2 "error": {
3 "type": "invalid_request_error",
4 "code": "invalid_string",
5 "message": "Invalid datetime",
6 "field": "cursor"
7 }
8}

Walking a list end to end

1async function* walkCustomers() {
2 let cursor: string | undefined;
3 while (true) {
4 const url = new URL("https://my.pivotal.app/api/v1/customers");
5 url.searchParams.set("limit", "100");
6 if (cursor) url.searchParams.set("cursor", cursor);
7
8 const res = await fetch(url, {
9 headers: { Authorization: `Bearer ${process.env.PIVOTAL_API_KEY}` },
10 });
11 const page = await res.json();
12 for (const c of page.data) yield c;
13
14 if (!page.has_more) return;
15 cursor = page.next_cursor;
16 }
17}
18
19for await (const customer of walkCustomers()) {
20 console.log(customer.display_id, customer.name);
21}

Python:

1def walk_customers():
2 cursor = None
3 while True:
4 params = {"limit": 100}
5 if cursor:
6 params["cursor"] = cursor
7 res = requests.get(
8 "https://my.pivotal.app/api/v1/customers",
9 params=params,
10 headers={"Authorization": f"Bearer {os.environ['PIVOTAL_API_KEY']}"},
11 )
12 page = res.json()
13 for c in page["data"]:
14 yield c
15 if not page["has_more"]:
16 return
17 cursor = page["next_cursor"]

Filters and pagination interact

Filters like ?status=active apply on the server, then pagination cuts the filtered result. The cursor still walks created_at descending — you don’t get drift from cursoring through a moving filter as long as the filter is stable for the duration of the walk.

If you mutate records mid-walk (e.g. flipping status from onboarding to active while paginating ?status=onboarding), expect to either skip or duplicate the affected rows depending on the timing. Snapshot in your client if that’s a problem.

What we deliberately don’t do

  • No total count. Counting at scale gets expensive. If you need a UI element that says “245 customers”, fetch the count separately (a future /customers/count endpoint will land — for now, walk the list once and cache).
  • No offset/limit. Offsets get slower as the offset grows and behave badly with concurrent inserts. Cursors are stable and constant-cost.
  • No before cursor. Lists return newest first; if you need older-first, reverse the page in your client.

Edge cases

  • An empty page comes back as { object: "list", data: [], has_more: false, next_cursor: null }.
  • The last page has has_more: false even if it returned limit items — trust the flag, not the count.
  • Soft-deleted rows never appear in lists. They’re still gettable by id if you know it (GET /customers/{id} will 404 — soft-deleted is treated as gone). If you need to read deletes, surface them through the customer’s timeline.