What this is and who needs it

Any list endpoint — customers, orders, transactions, subscriptions — returns at most a single page of results. To see the rest you have to ask for the next page. EPD uses cursor pagination, which is stable even when new records are being added in real time.

If you build dashboards, run exports, or sync EPD data into your warehouse, you need this page.

Request parameters

ParameterTypeDefaultDescription
limitinteger10Page size, between 1 and 100.
starting_afterstringCursor of the last item on the previous page; returns items after it.
ending_beforestringCursor of the first item on the next page; returns items before it.

Send starting_after or ending_before, never both.

Response shape

{
  "object": "list",
  "data": [
    /* up to `limit` items */
  ],
  "has_more": true,
  "url": "/v1/customers",
  "total_count": 1247,
  "cursors": {
    "next": "550e8400-e29b-41d4-a716-446655440000",
    "previous": null
  }
}
FieldDescription
objectAlways "list".
dataArray of resources for this page.
has_moretrue if more results exist beyond this page in the current direction.
urlEndpoint path that produced this response.
total_countTotal matching rows. Returned when feasible — do not assume it is always present.
cursors.nextPass as starting_after to fetch the next page. null on the last page.
cursors.previousPass as ending_before to fetch the previous page. null on the first page.

Walking every page

let cursor;
do {
  const url = new URL("https://api.epd.com/v1/customers");
  url.searchParams.set("limit", "100");
  if (cursor) url.searchParams.set("starting_after", cursor);

  const res = await fetch(url, {
    headers: {
      Authorization: `Bearer ${process.env.EPD_KEY}`,
      "epd-version": "2026-02-11",
    },
  });
  const page = await res.json();

  for (const customer of page.data) {
    // process
  }
  cursor = page.has_more ? page.cursors.next : undefined;
} while (cursor);
import os, requests

cursor = None
while True:
    params = {"limit": 100}
    if cursor:
        params["starting_after"] = cursor

    r = requests.get(
        "https://api.epd.com/v1/customers",
        params=params,
        headers={
            "Authorization": f"Bearer {os.environ['EPD_KEY']}",
            "epd-version": "2026-02-11",
        },
    )
    page = r.json()

    for customer in page["data"]:
        # process
        ...

    if not page["has_more"]:
        break
    cursor = page["cursors"]["next"]

Cursor values are the id of the last (or first) item in the page — UUIDs today. Treat them as opaque: do not parse them, do not assume the format will stay this way, and do not persist them past the lifetime of one walk.

For exports, prefer limit=100 to minimize round-trips. For interactive UIs, prefer limit=20 and let the user pull more.