Queries and terminals
Python JQL roots, time bounds, select terminals, and result row shapes.
Root builders
traces(filter=None) -> QueryBuilder
spans(filter=None) -> QueryBuilder
sessions(filter=None) -> QueryBuilder| Source | Grain | Canonical fields |
|---|---|---|
traces | One row per trace | trace_id, span_id, span_name, name, input, output, duration, status, timestamp, session, customer, error |
spans | One row per span | Common fields plus cost, model, parent_span_id |
sessions | One row per session | Common fields plus error |
Field catalogs can evolve. Use client.discover("fields", source="spans") before generating
dynamic query UIs or relying on project-specific attributes.
Builder methods
.where(filter) -> QueryBuilder
.last(window) -> QueryBuilder
.since(since) -> QueryBuilder
.between(start, end) -> QueryBuilder
.rows(*, fields=None, limit=None) -> QueryBuilder
.ids() -> QueryBuilder
.count(by=None) -> QueryBuilder
.recent(n) -> QueryBuilder
.top(n, by) -> QueryBuilder
.ranked(*, by=None, pick=None, within=None) -> QueryBuilder
.agg(func, field, q=None) -> QueryBuilder
.trend(*, metric=None, bucket=None) -> QueryBuilder
.pipe() -> PipelineBuilder
.to_json() -> dict[str, Any]Builders are immutable: every method returns a new builder. Calling .where()
multiple times combines the root filters with canonical all (AND). Calling
.last(), .since(), or .between() replaces any earlier time bound.
Serialize without executing with query.to_json().
Time object
The JSON time object must contain exactly one key:
| Builder | JSON | Input |
|---|---|---|
.last(window) | {"last":"7d"} | Relative duration string |
.since(since) | {"since":"2026-07-01"} | Date/time string accepted by the server |
.between(start, end) | {"between":["2026-07-01","2026-07-08"]} | Exactly two date/time strings |
Omitting time searches all available history unless a deployment-level policy
adds a default window.
Select terminals
A source query can contain at most one select. In Python, a second select raises ValueError in the builder. The server
also rejects a query containing both select and pipe.
| Terminal | Inputs and defaults | Result rows |
|---|---|---|
rows | fields?: string[], limit?: number | Requested fields, or the source's default row projection |
ids | None | Distinct ID columns for the grain: traces returns trace_id; spans returns both trace_id and span_id; sessions returns session_id |
count | by?: string | No by: {"count": number}; with by: {"key": value, "count": number} |
recent | n: int | Source rows ordered by newest timestamp |
top | n: int, by: string | Source rows ordered by the numeric field descending |
ranked | within?: string, by="timestamp", pick=1 | Traces/spans only. Positional rows plus _rn; pick is a nonzero rank or two-item range 1 <= a <= b |
agg | func, field, q? | Traces/spans only. Exactly one row: {"value": number | null} |
trend | metric="count", bucket="1d" | Count: {"bucket": datetime, "n": number}; rate: adds total and rate |
Aggregate rules
func is one of avg, sum, min, max, quantile, or
count_distinct. field is always required. q is required only for
quantile, rejected for every other function, and must satisfy 0 < q < 1.
Trend rules
metric is count or rate. bucket is a duration string such as
1d or 1w. Rate trends require a filter that defines the numerator.