Pagination & filtering

The three list conventions the Fungies API uses, plus sorting and archived-record semantics.

List endpoints don't all paginate the same way. The Fungies API ships three conventions, and which one an endpoint uses depends on the resource. Check this page before you write a paging loop.

The array of results is keyed by the resource name, not a generic items — data.orders, data.products, data.subscriptions, and so on. The exact key for each endpoint is in its reference page.

1. Offset pagination — skip / take

The most common convention. Pass skip (how many records to step over) and take (page size).

EndpointResult key
GET /v0/orders/listorders
GET /v0/payments/listpayments
GET /v0/products/listproducts
GET /v0/offers/listoffers
GET /v0/discounts/listdiscounts
GET /v0/elements/checkout/listcheckoutElements
GET /v0/webhooks/{webhookId}/attemptsattempts
curl "https://api.fungies.io/v0/orders/list?skip=0&take=20&returnCount=true" \
  -H "x-fngs-public-key: pub_your_public_key"
{
  "status": "success",
  "data": {
    "orders": [{ "object": "order", "id": "550e8400-e29b-41d4-a716-446655440000" }],
    "count": 143
  }
}

take has a default and a cap

On orders, payments, products, and offers, take defaults to 20 and is capped at 100. GET /v0/elements/checkout/list and GET /v0/webhooks/{webhookId}/attempts default to 10, also capped at 100.

Omitting take gives you 20 records, not the whole result set — and nothing in the response says so, since count is null unless you ask for it. If you are exporting or reconciling, always page explicitly.

Asking for more than the cap is not an error: take=500 returns the first 100 records with a 200. Because the response looks like any other full page, a loop that assumes it received 500 rows will skip the ones in between — step skip by the number of records you actually got back, not by the number you asked for. A take below 1 is raised to 1.

count is opt-in

On orders, payments, products, offers, and discounts, count is null unless you pass returnCount=true. Counting is a second query, so it's off by default. If you're driving a "page 3 of 8" UI, ask for it; if you're just walking pages, don't.

GET /v0/elements/checkout/list and GET /v0/webhooks/{webhookId}/attempts don't take returnCount — they always return a count.

Without a count, page until a response comes back with fewer records than you asked for:

const take = 100;
let skip = 0;

for (;;) {
  const url = `https://api.fungies.io/v0/orders/list?skip=${skip}&take=${take}`;
  const { data } = await get(url);

  yield* data.orders;

  if (data.orders.length < take) break;
  skip += take;
}

2. Page pagination — page / limit

Used by the user endpoints. page is 1-based, and the count is always returned, together with the total number of pages.

EndpointResult key
GET /v0/users/listusers
GET /v0/users/{userId}/inventoryinventory
curl "https://api.fungies.io/v0/users/list?page=1&limit=25" \
  -H "x-fngs-public-key: pub_your_public_key"
{
  "status": "success",
  "data": {
    "users": [{ "object": "user", "id": "550e8400-e29b-41d4-a716-446655440000" }],
    "count": 143,
    "pages": 6
  }
}

Stop when page reaches pages.

3. Cursor pagination — cursor / take

Only GET /v0/subscriptions/list. Pass take (1–100, default 10) and, from the second page on, the cursor returned by the previous response.

{
  "status": "success",
  "data": {
    "subscriptions": [{ "object": "subscription", "id": "550e8400-e29b-41d4-a716-446655440000" }],
    "count": 10,
    "cursor": "c3Vic2NyaXB0aW9uXzEyMw",
    "hasMore": true
  }
}

Loop on hasMore, not on count — count is a total where one is available, and it can be null.

let cursor;

for (;;) {
  const query = new URLSearchParams({ take: "50", ...(cursor ? { cursor } : {}) });
  const { data } = await get(`https://api.fungies.io/v0/subscriptions/list?${query}`);

  yield* data.subscriptions;

  if (!data.hasMore || !data.cursor) break;
  cursor = data.cursor;
}

GET /v0/webhooks/list is not paginated at all — it returns every webhook on the workspace in one response.

Sorting

Most offset-paginated endpoints accept orderDirection:

  • ASC — ascending (oldest or lowest first)
  • DESC — descending (newest or highest first)

The resource lists also accept orderBy to choose the field. Accepted values differ per resource, and a few endpoints (such as GET /v0/elements/checkout/list) have a fixed order and take neither parameter — check the endpoint's reference page.

Sort order is not a stable snapshot. Records created while you're paging can shift rows between pages. For a consistent export, pin a window with the endpoint's createdFrom / createdTo filters.

Archived records

Archiving is a soft delete — the record stays readable but drops out of normal listings. Two independent flags give you three behaviours:

QueryReturns
(neither flag)Active records only — the default.
archived=trueOnly archived records.
withArchived=trueActive and archived records together.

archived=true is not "include archived" — it is "archived only". If you want both, use withArchived=true. When both flags are sent, archived=true wins and you get archived records only.

This is also why a GET by id can return 404 right after you archive something: the record still exists, but the default read excludes it.

Filtering

Filters are per-resource query parameters — status, currency, country, date windows, custom fields, and more. They combine with AND. See each endpoint's reference page for the full list.

Two things catch people out on order, payment, and subscription filters:

  • Numbers are matched verbatim. The dashboard shows a leading #; it is not part of the stored value. Send L8VQK3N2M7KpQ9nR, not #L8VQK3N2M7KpQ9nR, or the filter returns no rows.
  • Money is in the smallest currency unit. valueFrom=1000 means 10.00 USD, and ¥1000 in JPY.

Last updated