This document describes how to search and filter resources in the HyperFleet API using TSL (Tree Search Language) queries.
The HyperFleet API supports search and filtering capabilities through the search query parameter. All list endpoints (GET /clusters, GET /nodepools, etc.) accept TSL (Tree Search Language) queries that allow you to filter results using field comparisons, logical operators, and complex nested conditions.
The HyperFleet API uses the Tree Search Language (TSL) library for parsing search queries.
| Operator | Description | Example |
|---|---|---|
= |
Equal | name='test' |
!= |
Not equal | name!='old' |
< |
Less than | generation<10 |
<= |
Less than or equal | generation<=5 |
> |
Greater than | generation>1 |
>= |
Greater than or equal | generation>=1 |
in |
In list | name in ('c1','c2') |
and |
Logical AND | a='1' and b='2' |
or |
Logical OR | a='1' or a='2' |
not |
Logical NOT | not name='test' |
- String values: Must be enclosed in single quotes:
name='my-cluster' - Numeric values: No quotes required:
generation>5 - Lists: Comma-separated values in parentheses:
id in ('2abc123', '2def456')
| Field | Type | Description | Example |
|---|---|---|---|
id |
string | Cluster ID | id='2abc123' |
name |
string | Cluster name | name='my-cluster' |
generation |
integer | Spec version counter | generation>1 |
created_by |
string | Creator email | created_by='user@example.com' |
updated_by |
string | Last updater email | updated_by='user@example.com' |
labels.<key> |
string | Label value | labels.environment='production' |
status.conditions.<Type> |
string | Condition status | status.conditions.Ready='True' |
status.conditions.<Type>.<Subfield> |
varies | Condition subfield | status.conditions.Ready.last_updated_time < '...' |
# Find cluster by name
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=name='my-cluster'"
# Find clusters by multiple names
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=name in ('cluster1', 'cluster2', 'cluster3')"NodePools support the same searchable fields as Clusters, plus:
| Field | Type | Description | Example |
|---|---|---|---|
owner_id |
string | Parent cluster ID | owner_id='2abc123' |
# Find nodepools by parent cluster ID
curl -G "http://localhost:8000/api/hyperfleet/v1/nodepools" \
--data-urlencode "search=owner_id='2abc123'"
# Find ready nodepools
curl -G "http://localhost:8000/api/hyperfleet/v1/nodepools" \
--data-urlencode "search=status.conditions.Ready='True'"
# Find nodepools by label
curl -G "http://localhost:8000/api/hyperfleet/v1/nodepools" \
--data-urlencode "search=labels.role='worker'"Use labels.<key> syntax to filter by label values:
# Find production clusters
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=labels.environment='production'"
# Find clusters in a specific region
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=labels.region='us-east'"Label keys must contain only lowercase letters (a-z), digits (0-9), and underscores (_).
Query resources by status conditions: status.conditions.<Type>='<Status>'
Condition types must be PascalCase (Ready, Available) and status must be True or False for resource conditions.
Note: Only the = operator is supported for condition queries. Other operators (!=, <, >, in, etc.) will return an error. The NOT operator is not supported with condition queries (status.conditions.<Type> or status.conditions.<Type>.<Subfield>) and will return a 400 Bad Request error. Use the inverse condition value instead (e.g., status.conditions.Ready='False' rather than NOT status.conditions.Ready='True').
# Find available clusters
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Available='True'"
# Find clusters that are not ready
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready='False'"Query resources by condition subfields such as timestamps and observed generation:
status.conditions.<Type>.<Subfield> <op> '<Value>'
| Subfield | Type | Description |
|---|---|---|
last_updated_time |
TIMESTAMPTZ | When the condition was last updated |
last_transition_time |
TIMESTAMPTZ | When the condition status last changed |
observed_generation |
INTEGER | Last generation processed by the condition |
Condition subfields support comparison operators: =, !=, <, <=, >, >=.
Note:
status.conditions.<Type>(without subfield) only supports the=operator. TheNOToperator is not supported with any condition expression — neitherstatus.conditions.<Type>norstatus.conditions.<Type>.<Subfield>(e.g.,status.conditions.Ready.last_updated_time). UsingNOTwith these expressions returns a400 Bad Requesterror. Restructure queries usingAND/ORor the inverse condition value instead.
# Find clusters where Ready condition hasn't been updated in the last hour
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready.last_updated_time < '2026-03-06T14:00:00Z'"
# Find stale-ready resources (Sentinel selective polling use case)
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready='True' AND status.conditions.Ready.last_updated_time < '2026-03-06T14:00:00Z'"
# Find clusters with observed_generation below a threshold (uses unquoted integer)
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready.observed_generation < 5"Time subfields require RFC3339 format (e.g., 2026-01-01T00:00:00Z). Integer subfields use unquoted numeric values.
Combine multiple conditions using and, or, not, and parentheses ():
# Find ready production clusters
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready='True' and labels.environment='production'"
# Find clusters in dev or staging
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=labels.environment in ('dev', 'staging')"
# Find ready clusters in production or staging
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=status.conditions.Ready='True' and (labels.environment='production' or labels.environment='staging')"
# Find clusters that are not in production
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=not labels.environment='production'"Operator precedence: () > comparisons > not > and > or
# Find non-production clusters
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=labels.environment in ('dev', 'staging', 'test')"
# Find clusters created by specific user
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=created_by='user@example.com'"
# Find clusters by multiple IDs
curl -G "http://localhost:8000/api/hyperfleet/v1/clusters" \
--data-urlencode "search=id in ('2abc123', '2def456', '2ghi789')"Invalid queries return 400 Bad Request with error details:
{
"type": "https://api.hyperfleet.io/errors/bad-request",
"title": "Bad Request",
"status": 400,
"detail": "Failed to parse search query: invalid-query",
"code": "HYPERFLEET-VAL-005",
"timestamp": "2025-01-15T10:30:00Z"
}