Skip to content

Pagination

Cursor pagination for list endpoints — limit, starting_after, ending_before and has_more.

List endpoints (GET /orders, GET /products, GET /catalog/products, GET /events, …) return a list object and use cursor pagination:

JSON
{
  "object": "list",
  "data": [ { "object": "order", "id": "ord_01k8x4b2q9w7e3r5t1y8u6i0pa" } ],
  "has_more": true,
  "url": "/api/v1/orders"
}
Parameter Meaning
limit Objects per page, 1–100 (default 20).
starting_after An object id: return the page after it (the next page). Use the id of the last object you received.
ending_before An object id: return the page before it (the previous page). Use the id of the first object of the current page.

Use one cursor at a time (400 parameter_invalid if you send both). has_more tells you whether another page exists in the direction you are paging. Lists are newest first (except order events, which are oldest first). Filters (for example status on orders) combine with cursors — keep the same filters while paging.

Query parameters are strict: a parameter an endpoint does not document is rejected with 400 parameter_invalid naming it (Received unknown parameter: updated_since (query parameter).), so a misspelled filter never silently returns everything. The one exception is _, which every endpoint accepts and ignores (for cache-busters added by HTTP tools).

Cursors

A cursor is the id of an object of the same list, so it carries the list's id prefix:

List Cursor prefix
GET /orders ord_
GET /products prod_
GET /catalog/products cp_
GET /files file_
GET /events evt_
GET /wallet/transactions txn_
GET /topups top_
GET /webhook_endpoints we_

A cursor with another prefix (for example a product id on /orders) is a 400 parameter_invalid on starting_after / ending_before. A well-formed id that does not exist (an object deleted since) is accepted: the page simply starts after (or ends before) where that id would sort. The catalog is the exception — its order is curated (featured first), so /catalog/products cursors must be ids of products in the catalog.

Iterate over everything

async function* listAll(path, params = {}) {
  let startingAfter;
  while (true) {
    const query = new URLSearchParams({ ...params, limit: "100", ...(startingAfter ? { starting_after: startingAfter } : {}) });
    const res = await fetch(`https://swaglists.com/api/v1${path}?${query}`, { headers: { Authorization: `Bearer ${process.env.API_KEY}` } });
    const page = await res.json();
    if (!res.ok) throw new Error(`${page.error.code}: ${page.error.message}`);
    yield* page.data;
    if (!page.has_more || page.data.length === 0) return;
    startingAfter = page.data.at(-1).id;
  }
}

for await (const order of listAll("/orders", { status: "on_hold" })) {
  console.log(order.id, order.hold_reason);
}

Polling for new objects

To fetch only what is new since your last sync, page backwards from the newest object you already have: GET /events?ending_before=<newest event id you processed> returns newer events. Or filter by time: GET /orders?created[gte]=2026-10-20T00:00:00Z (see time filters). For near-real-time updates, prefer webhooks.

Time filters

created[gte] (at or after) and created[lte] (at or before) on GET /orders and GET /events accept:

Format Example Meaning
Unix seconds 1761955200 Seconds since 1970-01-01 UTC (fractions allowed).
Unix milliseconds 1761955200000 Any number above 100000000000 is read as milliseconds.
ISO-8601 date 2026-10-01 Midnight UTC of that day.
ISO-8601 date-time with an offset 2026-10-01T09:30:00Z, 2026-10-01T11:30:00+02:00 That instant.
ISO-8601 date-time without an offset 2026-10-01T09:30:00 Read as UTC — never as the server's or your local time.

URL-encode + offsets (%2B02:00); an unencoded + (which query strings turn into a space) is understood as well. Anything else — other formats, impossible dates such as 2026-02-30, years outside 2000–2100 — is a 400 parameter_invalid on the parameter.

Full collections

A few endpoints return a complete collection in the same list shape with has_more: false and no paging: GET /catalog/categories, GET /products/{id}/variants, GET /orders/{id}/events and POST /shipping/rates.