> ## Documentation Index
> Fetch the complete documentation index at: https://docs.honeycomb.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference: Query Specification

> Define Honeycomb queries in JSON to use with the Query API, Boards API, Triggers API, and query template links. Reference for supported syntax and structure.

We support defining queries via JSON.

You can use defined queries in Honeycomb with:

* [Query Template Links](/investigate/collaborate/share-query/)
* the [Query API](/api/queries/)
* the [Boards API](/api/boards/)
* the [Triggers API](/api/triggers/)

<Note>
  To convert query JSON into a sharable query URL, refer to [Query Template Links](/investigate/collaborate/share-query/#creating-a-query-template-link-manually) documentation.
</Note>

## Fields on a Query Specification

All fields are optional, but a query without any `calculations` values will have `COUNT` applied automatically.

* `breakdowns`: a list of strings describing the columns by which to break events down into groups
* `calculations`: a list of objects describing the calculations to return as a time series and summary table. Each calculation consists of an `op` and a `column` (except for `COUNT` or `CONCURRENCY`, which need no column). If no `calculations` are provided, `COUNT` is applied. [See below for a list of valid `op`s](#calculation-operators).
* `filters`: a list of objects describing the filters with which to restrict the considered events. Each filter consists of a `column`, `op`, and (sometimes) `value`. [See below for a list of valid `op`s](#filter-operators).
* `filter_combination`: either `"AND"` or `"OR"`. If multiple filters are specified, `filter_combination` determines how they are applied; set to `"OR"` to match ANY filter in the filter list. Defaults to `"AND"`.
* `granularity`: an integer describing the time resolution of the query's graph, in seconds. Valid values are the query's time range /10 at maximum, and /1000 at minimum.
* `orders`: a list of objects describing the terms on which to order the query results. Each term must appear in either the `breakdowns` field or the `calculations` field.
* `limit`: an integer describing the maximum number of query results.
* `havings`: a list of objects describing filters with which to restrict returned groups. Each `having` consists of a `calculate_op` (the same set used for `op` in `calculations`, but excluding `HEATMAP`), a `column` (except for `COUNT`, which needs no column), an op (`=`, `>`, `>=`, `<`, `<=`), and a `value` (currently assumed to be numeric). Each `column`/`calculate_op` pair must appear in the `calculations` field. There can be multiple `havings` for the same `column`/`calculate_op` pair.
* `time_range`: an integer describing the time range of query in seconds, for relative-time queries. Cannot be combined with both `start_time` and `end_time` ([See caveat below](#how-to-specify-an-absolute-time-range).) Defaults to two hours.
* `start_time`: an integer describing the UNIX timestamp of the absolute start time of the query.
* `end_time`: an integer describing the UNIX timestamp of the absolute end time of the query.
* `calculated_fields`: a list of objects defining the [calculated fields](/investigate/query/build/calculated-fields/) that are computed from your data using expressions. Each calculated field consists of a `name` (used to reference it in calculations, filters, or ordering) and an `expression` (a formula that derives its value from existing fields).

## How to Specify an Absolute Time Range

To specify an absolute time range, use `start_time` and `end_time` (both UNIX timestamps) to describe the desired range:

```json theme={}
{
  "start_time": 1727755200,
  "end_time": 1728964800
}
```

`start_time` or `end_time` can also be used in combination with `time_range` to specify a fixed time plus a range.
Specifying values for all three fields, however, will result in an error.

The following describes "the one-hour period leading up to the absolute time 2024-05-01 00:00 UTC":

```json theme={}
{
  "end_time": 1714536000,
  "time_range": 3600
}
```

Without a `start_time` or `end_time`, a single `time_range` argument will default to an `end_time` of "now."

The following describes "the last hour" relative to the time of invocation:

```json theme={}
{
  "time_range": 3600
}
```

## Examples

### Top Ten Unique `user_agents` by Volume

This query specification calculates the top 10 (by volume, or `COUNT`) unique `user_agents` for the hour leading up to the absolute time 2024-01-01 00:00 UTC:

#### Query Builder Query

| VISUALIZE | WHERE | GROUP BY     |
| --------- | ----- | ------------ |
| COUNT     |       | `user_agent` |

Use the [time picker](/investigate/query/build/#change-and-compare-query-time-ranges) to apply "Last 1 Hour".

#### JSON

```json theme={}
{
  "breakdowns": ["user_agent"],
  "calculations": [{ "op": "COUNT" }],
  "orders": [{ "op": "COUNT", "order": "descending" }],
  "limit": 10,
  "time_range": 3600,
  "end_time": 1704085200
}
```

### Average Content Length of Events

This query specification calculates the `AVG(content_length)` of events from the last hour matching `kafka_partition = 3` or `kafka_partition = 6`:

#### Query Builder Query

| VISUALIZE             | WHERE                                               |
| --------------------- | --------------------------------------------------- |
| AVG (content\_length) | kafka\_partition = 3 OR <br /> kafka\_partition = 6 |

```json theme={}
{
  "calculations": [{ "column": "content_length", "op": "AVG" }],
  "filters": [
    { "column": "kafka_partition", "op": "=", "value": 3 },
    { "column": "kafka_partition", "op": "=", "value": 6 }
  ],
  "filter_combination": "OR",
  "time_range": 3600
}
```

### Filter Events Based on Length of Duration and Service Name

This query specification matches events with `duration_ms > 500` and `service.name != "fraud"`, then calculates a `HEATMAP(match_quality)` (where the graph spans the last 3 hours, drawn at 15-minute intervals):

#### Query Builder Query

| VISUALIZE               | WHERE                                             |
| ----------------------- | ------------------------------------------------- |
| HEATMAP(match\_quality) | duration\_ms > 500 <br /> service.name != "fraud" |

#### JSON

```json theme={}
{
  "calculations": [{ "column": "match_quality", "op": "HEATMAP" }],
  "filters": [
    { "column": "duration_ms", "op": ">", "value": 500 },
    { "column": "service.name", "op": "!=", "value": "fraud" }
  ],
  "granularity": 900,
  "time_range": 10800
}
```

### Top 100 Results for Average Duration and 99th Percentile of Event Duration Ranked by Duration and User

This query specification describes an absolute-time query which matches events with `result = 200`, then calculates the `AVG(duration_ms)` and `P99(duration_ms)` broken down into unique (`user_id`, `build_id`) pairs. It then returns the first 100 results, ordered first by `P99(duration_ms)` values, then `user_id`.

#### Query Builder Query

| VISUALIZE                                  | WHERE        | GROUP BY                    |
| ------------------------------------------ | ------------ | --------------------------- |
| AVG(duration\_ms) <br /> P99(duration\_ms) | result = 200 | `user_id` <br /> `build_id` |

| ORDER BY                                    | LIMIT |
| ------------------------------------------- | ----- |
| P99(duration\_ms) desc <br /> user\_id desc | 100   |

#### JSON

```json theme={}
{
  "breakdowns": ["user_id", "build_id"],
  "calculations": [
    { "column": "duration_ms", "op": "AVG" },
    { "column": "duration_ms", "op": "P99" }
  ],
  "filters": [{ "column": "result", "op": "=", "value": 200 }],
  "orders": [
    { "column": "duration_ms", "op": "P99", "order": "descending" },
    { "column": "user_id" }
  ],
  "limit": 100,
  "start_time": 1515542451,
  "end_time": 1515546051
}
```

### Top 100 Results for Events with a Duration With Longer Than 10000 Milliseconds

This query specification describes a query over the last hour that matches events with `result = 200`, then calculates the `AVG(duration_ms)` and `P99(duration_ms)` broken down into unique (`user_id`, `build_id`) pairs. It then returns the first 100 results that have a `P99(duration_ms) > 10000`, ordered first by `P99(duration_ms)` values, then `user_id`.

#### Query Builder Query

| VISUALIZE                                  | WHERE        | GROUP BY                  |
| ------------------------------------------ | ------------ | ------------------------- |
| AVG(duration\_ms) <br /> P99(duration\_ms) | result = 200 | user\_id <br /> build\_id |

| ORDER BY                               | LIMIT | HAVING                    |
| -------------------------------------- | ----- | ------------------------- |
| P99(duration\_ms) desc <br /> user\_id | 100   | P99(duration\_ms) > 10000 |

#### JSON

```json theme={}
{
  "breakdowns": ["user_id", "build_id"],
  "calculations": [
    { "column": "duration_ms", "op": "AVG" },
    { "column": "duration_ms", "op": "P99" }
  ],
  "filters": [{ "column": "result", "op": "=", "value": 200 }],
  "orders": [
    { "column": "duration_ms", "op": "P99", "order": "descending" },
    { "column": "user_id" }
  ],
  "havings": [
    { "calculate_op": "P99", "column": "duration_ms", "op": ">", "value": 10000 }
  ],
  "limit": 100,
  "time_range": 3600
}
```

### Match Specific Users and Identify Experienced Errors

This query specification describes a query that filters for specific users based on their email address who are experiencing errors broken down by error code.

#### Query Builder Query

| VISUALIZE | WHERE                                                                                                                      | GROUP BY   |
| --------- | -------------------------------------------------------------------------------------------------------------------------- | ---------- |
| COUNT     | app.user.email in [foo@example.com](mailto:foo@example.com), [bar@example.com](mailto:bar@example.com) <br /> error exists | error.code |

#### JSON

```json theme={}
{
    "breakdowns": [
        "error.code"
    ],
    "calculations": [
        { "op": "COUNT" }
    ],
    "filters": [
        { "column": "app.email", "op": "in", "value": ["foo@example.com", "bar@example.com"]},
        { "column": "error", "op": "exists"}
    ],
    "filter_combination": "AND"
}
```

## Calculation Operators

Calculation operators are consistent between the API and the UI.
The available calculation `"op"` values are:

* `COUNT` (optionally expects an accompanying `"column"` value)
* `CONCURRENCY` (does not expect an accompanying `"column"` value)
* `SUM`
* `AVG`
* `COUNT_DISTINCT`
* `MAX`
* `MIN`
* `P001`
* `P01`
* `P05`
* `P10`
* `P20`
* `P25`
* `P50`
* `P75`
* `P80`
* `P90`
* `P95`
* `P99`
* `P999`
* `HEATMAP`
* `RATE_AVG`
* `RATE_SUM`
* `RATE_MAX`

## Filter Operators

Filter operators are consistent between the API and the UI.
The available filter `"op"` values are:

* `=`
* `!=`
* `>`
* `>=`
* `<`
* `<=`
* `starts-with`
* `does-not-start-with`
* `ends-with`
* `does-not-end-with`
* `exists` (does not expect an accompanying `"value"`)
* `does-not-exist` (does not expect an accompanying `"value"`)
* `contains`
* `does-not-contain`
* `in` (filter `"value"` must be present and be an array - [example](#match-specific-users-and-identify-experienced-errors))
* `not-in` (filter `"value"` must be present and be an array)
