, ,

Filtering, Pagination, and Projections: Querying the Nutanix API Efficiently

4 min read

There is a script pattern that appears in almost every Nutanix environment eventually. It lists every VM in Prism Central, loops through the results in memory, and keeps the handful that match some condition. It works perfectly in a lab of twenty VMs and becomes a problem at two thousand.

The v4 APIs adopt OData query conventions, which means the filtering, sorting, and field selection can happen on the server instead. The result is less data on the wire, less memory used, and a query you can read.

Applies to: Prism Central v4 GA APIs. Supported query options and filterable fields vary by endpoint; check the reference for the specific API you are calling.

The query options you will actually use

OptionPurpose
$filterReturn only entities matching an expression
$orderbySort ascending (asc) or descending (desc)
$selectReturn only the named properties
$pageWhich page of results to return
$limitHow many records per page
$expandInclude related entities inline

Two constraints are worth committing to memory. $page is zero-based, so the first page is 0 rather than 1. And $limit accepts values from 1 to 100; if you omit it, a default page size applies, commonly 50.

A value outside the valid $page range does not raise an error; it simply returns nothing. An empty result set is therefore not proof that nothing matched.

Filtering on the server: Filter expressions follow OData URL conventions, so equality and string functions look like this:

# Exact match on name
?$filter=name eq 'app-server-01'

# Prefix match
?$filter=startswith(name, 'prod-')

# Combined with sorting and a page size
?$filter=startswith(name, 'prod-')&$orderby=name asc&$limit=50

Remember to URL-encode these when building requests programmatically. Spaces and single quotes inside a filter expression are a common source of confusing 400 responses.

Not every property is filterable, and the set differs between endpoints. The API reference lists the filterable and sortable fields per endpoint, and that list is the authority; guessing a field name usually produces an error rather than a silent fallback, which is at least honest.

Selection projection trims the response: If you are building a report that needs cluster names, there is no reason to transfer the full configuration of every cluster.

GET /api/clustermgmt/v4.0/config/clusters?$select=name

This is the single easiest optimization available. Inventory and reporting jobs frequently pull complete entity payloads and discard ninety percent of each one, and $select removes that waste with a one-line change.

Support for selection projection expanded across namespaces over successive releases, so if $select behaves unexpectedly on an endpoint, check whether your Prism Central version supports it there.

Paginate deliberately: With a maximum page size of 100, any collection larger than that requires paging. A reliable loop looks like this:

  1. Request page 0 with an explicit $limit.
  2. Process the returned records.
  3. Stop when a page returns fewer records than the limit, or none at all.
  4. Apply the same $filter and $orderby to every page.
  5. Pace the requests rather than firing them concurrently without limit.

Step four is not optional. Paging through an unsorted collection while entities are being created or deleted can return duplicates or skip records entirely. A stable $orderby makes the traversal predictable.

Step five matters because Prism Central pc.2024.1 and AOS 6.8 introduced rate limit enforcement. Paging aggressively through a large inventory is exactly the workload that meets those limits.

Aggregation without pulling the data: Where supported, the $apply option groups a collection by a property and computes aggregates over each group, with functions including minimum, maximum, sum, average, and count.

For any summary or roll-up view, this is far better than retrieving every record to count them locally. It is also worth checking before you write your own aggregation logic: the API may already do it.

Practical habits

  • Always set $limit explicitly rather than relying on the default
  • Add $select to every reporting query
  • Filter server-side before filtering client-side, not after
  • Log the full request URL, so a failed query can be replayed exactly
  • Test filters against a large data set, not just a lab

The logging habit pays for itself the first time a scheduled job fails at three in the morning. With OData the entire query is in the URL, so the log line is the reproduction case.

Summary: Push the work to the server. $filter narrows the result set, $select trims each record, $orderby makes paging stable, and $page with $limit controls the traversal, remembering that pages start at zero and the limit caps at 100.

Confirm which fields are filterable for the endpoint you are calling, and pace requests once a job grows beyond a few pages.

Series Links:

Official Resources

Nutanix v4 API IntroductionNutanix API User GuideNutanix Developer Portal


What Do You Think? Have you rewritten any client-side filtering as OData queries yet, and did it make a measurable difference at your scale?