Querying data

Paging

OfficeRnD API allows you to pass paging parameters in order to correctly traverse through a high number of results.
All v2 list endpoints use a uniform, cursor-based pagination scheme. You'll use a $limit parameter to control page size and receive $cursorNext tokens for fetching subsequent pages​. The default page size is 50

$limit parameter ( Optional )

The number of items to return in the response. The default (and maximum) is 50 items per request. If you do not specify $limit, the API will return 50 items by default. (Any value above 50 will be capped at 50.)

$cursorNext parameter ( Optional )

A cursor token indicating the page of results after a given item. Use this to retrieve the next page. For the first page of results, omit this parameter (or set it to null) to start from the beginning.

$cursorPrev parameter ( Optional )

A cursor token for the page of results before the given item. This is used to go one page backward (to the previous page), if needed.

Response Format and Pagination Metadata

The response body for list endpoints includes pagination metadata alongside the returned items.

{
  "results": [ … list of 50 transaction objects … ],
  "cursorNext": "eyJpZCI6IjUwIiwgIm9mZnNldCI6IjUwIn0=",  
  "cursorPrev": null,
  "rangeEnd": 50,
  "rangeStart": 1
}

In the example above, the results array contains up to 50 results. The cursorNext field holds a token you can use in the next request to get the following page of results. cursorPrev is null here, indicating this response was the first page (no previous page exists). On later pages, cursorPrev would contain a token to go backwards, and cursorNext would have a token (or be null if it was the last page).

Some endpoints may wrap this data in a top-level object or a pagination field – refer to the specific endpoint documentation in the technical spec for exact response shapes. Regardless of format, the presence of cursorNext and cursorPrev in the response gives you all the information needed to implement a “Next” or “Previous” button in your integration.

Example: Paginating Through Results

Fetch the first page: Make a request without any cursor (and optionally specify a limit, up to 50). For example:

> GET /api/v2/invoices?$limit=50

The API will return the first 50 invoices in an array, plus a cursorNext token in the JSON response (and cursorPrev will be null).

Fetch the next page: Take the cursorNext value from the first response, and use it in the next request. For example, if the first response returned "cursorNext": "abc123...", your next call would be:

> GET /api/v2/invoices?$limit=50&$cursorNext=abc123...

This retrieves the next 50 invoices. The response will include a new cursorNext (for page 3) and also a cursorPrev (which can be used to go back to page 1 if needed).

Repeat as needed: Continue this process until you reach a response where cursorNext is null, meaning you’ve reached the end of the list. Similarly, you can use cursorPrev to page backward if your application needs a “Previous” page button.

Each page request is independent: you don’t need to maintain any offset count yourself – just use the provided cursors. By adopting this approach, your integration benefits from efficient, predictable paging. The new system ensures you won’t accidentally skip or duplicate records due to intervening data changes, and it keeps the data retrieval snappy by never returning more than 50 items at once.

Filtering

API v2 introduces robust filtering features with a standardized query structure. In API v2, each endpoint defines a Filter DTO (Data Transfer Object) that outlines which fields can be filtered and how. These DTOs enforce consistency in queries, ensuring clients use only allowed fields (generally those indexed in the database for efficiency)​. By restricting filters to indexed fields, the API avoids expensive full table scans and significantly improves query performance​.

Supported filter operators: API v2 supports a concise set of operators for building queries, closely mirroring common database query syntax. You can use equality (implicitly, by specifying field=value), and the following operators within a filter JSON or query parameter structure:

  • $gt - greater than (>)

  • $gte - greater than or equal (>=)

  • $lt - less than (<)

  • $lte - less than or equal (<=)

  • $in - value is in an array of allowed values

These operators let you construct rich queries (e.g., numeric ranges, exclusions, multi-value matches) while keeping the format standardized. Multiple filters are combined with a logical AND by default, meaning a returned record must meet all conditions (this is the typical case for query parameters in REST).

For example, to retrieve members who have been created after 1st of January 2025, and have an "active" status, you could do:

GET /api/v2/organizations/{orgSlug}/members?createdAt[$gte]=2025-01-01T00:00:00.000Z&status=active

You can also mix operators on the same field to create ranges. For example, to fetch members created between the first of January 2025 and the first of February 2025 (inclusive of the first day of January, exclusive of the first day of February):

GET /api/v2/organizations/{orgSlug}/members?createdAt[$gte]=2025-01-01T00:00:00.000Z&createdAt[$lt]=2025-02-01T00:00:00.000Z

To filter on multiple values for a field, use the $in operator. For example, if a member resource has a status field, you can find members that are either "active" or "contact" with:

GET /api/v2/organizations/{orgSlug}/members?status[$in]=active,contact

(Depending on the API, multiple $in values might be provided as a comma-separated list or an array in JSON. Check the v2 docs for the exact syntax.) This query returns orders whose status is either "active" or "contact". Under the hood, the API will interpret the list and match any of the values.

Filtering limitations and rules: API v2 imposes some important limitations to keep queries performant and unambiguous:

  • Maximum of 50 values for $in: When using the $in operator, you can provide up to 50 values in the list. Supplying more than 50 values will result in an error (or will be ignored) in v2. This limitation is in place because extremely large $in lists can degrade database performance or even hit query parser limits. By capping it at 50, the API ensures the query remains efficient and avoids undue load​ (In practice, needing more than 50 exact matches might indicate a need for a different querying strategy or a dedicated endpoint for that use case.)

  • Mutually exclusive operators on the same field: You cannot use certain operator combinations together on one field. In particular, $gt and $gte cannot be applied to the same field in a single filter (since it wouldn’t make sense to have both “greater than” and “greater than or equal” on the same field simultaneously). Similarly, you can’t combine $lt and $lte on the same field. If both were provided, one would contradict or override the other, so v2 will reject such a query.
    The rule is to use one type of comparison per field: one of $gt/$gte (for the lower bound if needed), and one of $lt/$lte (for the upper bound if needed), along with $in if it applies. For example, instead of trying to do createdAt[$gt]=2025-01-01T00:00:00.000Z&createdAt[$gte]=2025-01-01T00:00:00.000Z (which is invalid), decide on the proper range – e.g., createdAt[$gte]=2025-01-01T00:00:00.000Z&createdAt[$lt]=2024-12-30T00:00:00.000Z. These constraints make the API’s behavior predictable and the filtering logic simpler to optimize.

  • No conflicting conditions: Building on the above, v2’s filter DTOs generally ensure you don’t provide conflicting conditions. For example, using $in on a field usually would exclude using $ne on the same field in the same query (since $in already specifies a set of acceptable values). The API will typically interpret multiple conditions on the same field as an AND combination, so if they conflict (no value can satisfy both), the result will just be empty, but it’s better to avoid such queries altogether. The DTO schema helps here by clearly defining what’s allowed.

Examples of valid filters:

  • Get all members who were created after 2024:
GET /api/v2/organizations/{orgSlug}/members?createdAt[$gte]=2024-01-01T00:00:00.000Z

This finds members that are created after January 1, 2024.

  • Get any members with status active or contact that were created in 2025:
GET /api/v2/organizations/{orgSlug}/members?status[$in]=active,contact&createdAt[$gte]=2024-01-01T00:00:00.000Z

These filtering capabilities in v2 are designed with performance and reliability in mind. By limiting filters to indexed fields and sane operator usage, the system can run queries using database indexes (which are very fast for lookups)​.

The operator restrictions (like not mixing $gt and $gte) prevent ambiguous queries and ensure the database can use a single range or equality condition per field, which is easier to optimize. Likewise, capping the $in array length avoids giant queries that could slow down the system​.