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

# SQL reference

> Query Braintrust logs, experiments, and datasets with SQL. Run queries from the sandbox, CLI, or API, and find your way around the SQL reference.

SQL queries in Braintrust provide a precise, standard syntax for querying Braintrust logs, experiments, and datasets. Use SQL to:

* Filter and search for relevant logs and experiments. Use [`WHERE` clauses](/reference/sql/query-structure#where) to filter individual records and [`HAVING` clauses](/reference/sql/query-structure#having-for-filtering-aggregations) to filter aggregated results after grouping.
* Create consistent, reusable queries for monitoring.
* Build automated reporting and analysis pipelines.
* Write complex queries to analyze model performance.

For tips on query performance and common pitfalls, see [SQL best practices](/reference/sql/best-practices).

<Note>
  **[Self-hosted deployments](/admin/self-hosting)**: SQL syntax support requires data plane version v1.1.29 or later.
</Note>

## Syntax styles

Braintrust supports two syntax styles: standard **SQL syntax**, and the legacy **BTQL syntax** with pipe-delimited clauses. SQL syntax is recommended for all new queries.

The parser automatically detects whether your query is SQL or BTQL:

* **SQL queries** start with `SELECT`, `WITH`, etc. followed by whitespace
* **BTQL queries** use clause syntax like `select:`, `filter:`, etc.

| SQL Clause                            | BTQL Clause                  |
| ------------------------------------- | ---------------------------- |
| `SELECT ...`                          | `select: ...`                |
| `FROM table('id', shape => 'traces')` | `from: table('id') traces`   |
| `WHERE ...`                           | `filter: ...`                |
| `GROUP BY ...`                        | `dimensions: ...`            |
| `GROUP BY ROLLUP(...)`                | `rollup: ...`                |
| `GROUP BY GROUPING SETS (...)`        | `grouping_sets: (...)`       |
| `HAVING ...`                          | `final_filter: ...`          |
| `TABLESAMPLE n PERCENT [SEED (seed)]` | `sample: n%`                 |
| `TABLESAMPLE n ROWS [SEED (seed)]`    | `sample: n`                  |
| `ORDER BY ...`                        | `sort: ...`                  |
| `LIMIT n`                             | `limit: n`                   |
| `OFFSET '<CURSOR_TOKEN>'`             | `cursor: '<CURSOR_TOKEN>'`   |
| `SETTINGS key = value, ...`           | `settings: key = value, ...` |

<Note>
  SQL syntax specifies the shape with a named parameter (e.g., `FROM experiment('id', shape => 'traces')`), while BTQL uses a trailing token (e.g., `from: experiment('id') traces`). Table aliases on top-level table functions (e.g., `FROM project_logs('id') AS t`) are reserved for future use. Aliases on subquery sources (e.g., `FROM (...) AS sub`) are required.
</Note>

<Note>
  **Full-text search:** Use the `MATCH` infix operator for full-text search:

  * `WHERE input MATCH 'search term'` → `filter: input MATCH 'search term'`
  * Multiple columns require OR: `WHERE input MATCH 'x' OR output MATCH 'x'` → `filter: input MATCH 'x' OR output MATCH 'x'`
</Note>

<Warning>
  **Unsupported SQL features:** The SQL parser does not support the following. For queries that require them, use BTQL's native syntax.

  * `JOIN`, `UNION`/`INTERSECT`/`EXCEPT`, and window functions.
  * [Subqueries](/reference/sql/query-structure#subqueries) in `WHERE`, `HAVING`, `SELECT`, or `PIVOT IN` (they are supported only in `FROM`).
  * `PIVOT` with explicit value lists, subqueries, or `ORDER BY` (only `IN (ANY)` is supported).
  * `INCLUDES` and `CONTAINS` operators. For exact array membership in SQL mode, use `IN`/`NOT IN` (for example, `tags IN ('value')`), or switch to BTQL's `filter: tags INCLUDES 'value'` syntax. `MATCH` is a fuzzy approximation.
</Warning>

## Run SQL queries

Run SQL from the SQL sandbox, the [`bt sql`](/reference/cli/sql) CLI, or the API.

### SQL sandbox

To test SQL with autocomplete, validation, and a table of results, use the [**<Icon icon="asterisk" /> SQL sandbox**](https://www.braintrust.dev/app/~/sql) in your project.

In the sandbox, you can use [**<Icon icon="blend" /> Loop**](/loop) to generate and optimize queries from natural language:

Example queries:

* "Find the most common errors in logs over the last week"
* "What are the highest scoring rows in my experiment"
* "Show me error distribution over time"
* "List all traces where latency exceeded 60 seconds"

Loop automatically populates the sandbox with the generated query, runs it, and provides a text summary of the results along with suggestions for additional queries.

Once you have a query in the sandbox, ask Loop to refine it:

* "Update the query to show error distribution over time"
* "Add a filter to only show errors from specific models"
* "Group by user instead"

When your query has errors, Loop can help fix them. Select **Fix with Loop** next to the error message in the sandbox. Loop analyzes the issue type and context to provide targeted fixes for:

* Syntax errors
* Schema validation issues
* Field name corrections

<Note>
  If a `project_logs()` query is missing a range filter on `created`, `_xact_id`, `_pagination_key`, or a specific `root_span_id`/`id`, the sandbox proactively warns you so you don't have to wait for a timeout to discover the issue.
</Note>

### bt CLI

Run SQL from your terminal with [`bt sql`](/reference/cli/sql). It opens an interactive editor, accepts an inline query, or reads from stdin for scripting.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
bt sql                                                       # Interactive editor
bt sql "SELECT * FROM project_logs('my-project') LIMIT 10"   # Inline query
cat query.sql | bt sql                                       # Pipe from a file
bt sql --non-interactive "SELECT count(*) FROM project_logs('my-project')"
```

### API

Access SQL programmatically with the Braintrust API:

<CodeGroup>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  curl -X POST https://api.braintrust.dev/btql \
    -H "Authorization: Bearer <YOUR_API_KEY>" \
    -H "Content-Type: application/json" \
    -d '{"query": "select: * | from: project_logs('"'<YOUR_PROJECT_ID>'"') | filter: tags INCLUDES '"'triage'"' AND created > now() - interval 7 day"}'
  ```
</CodeGroup>

The API accepts these parameters:

* `query` (required): your SQL query string.
* `fmt`: response format (`json` or `parquet`, defaults to `json`).
* `lint_mode`: root-level lint handling mode (`default` or `strict`, defaults to `default`). In `strict` mode, lint warnings fail the query.
* `tz_offset`: timezone offset in minutes for time-based operations.
* `audit_log`: include audit log data.
* `version`: an `_xact_id` string to query data as it existed at a specific point in time (useful for [recovering deleted rows](/kb/recovering-deleted-experiment-rows)). Supported for `experiment` and `dataset` sources; not supported for `project_logs`.

<Note>
  For correct day boundaries, set `tz_offset` to match your timezone. For example, use `480` for US Pacific Standard Time.
</Note>

## Next steps

* Learn the [query structure](/reference/sql/query-structure): clauses, data sources, data shapes, and field access.
* Look up syntax in [Functions and operators](/reference/sql/functions).
* Browse copy-and-run patterns in [Example queries](/reference/sql/examples).
* Write correct, fast queries with [SQL best practices](/reference/sql/best-practices).
