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

# bt sql

> Run SQL queries against Braintrust from your terminal

Run SQL queries against Braintrust. In a terminal, `bt sql` opens an interactive editor with query history. Pass a query directly or pipe from stdin for scripting.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
bt sql "SELECT * FROM project_logs('project-name') WHERE created > now() - interval 1 day LIMIT 5"
bt sql -p project-name "SELECT * FROM logs WHERE created > now() - interval 1 day LIMIT 5"
cat query.sql | bt sql
bt sql --non-interactive "SELECT count(*) FROM project_logs('project-name') WHERE created > now() - interval 1 day"
```

> [!TIP]
>
> Most SQL data-source functions also accept an object name in place of its ID. See [Querying by name](https://docs.braintrust.dev/reference/sql/query-structure.md#querying-by-name).

When a project is selected through [`bt switch`](https://docs.braintrust.dev/reference/cli/switch), `--project`, or `BRAINTRUST_DEFAULT_PROJECT`, use `FROM logs` as shorthand for that project's logs. `bt` resolves the project and replaces the shorthand with `project_logs('<project-id>')` before sending the query. Use `project_logs('<project-name-or-id>')` when the query should name a project directly instead of using the selected context.

## Interactive controls

| Key | Action |
| - | - |
| `↑` / `↓` | Navigate query history |
| `Enter` | Execute query |
| `Ctrl+c` | Clear input |
| `Ctrl+l` | Clear output |
| `Ctrl+d` / `Esc` | Exit |

## Query syntax

* A `FROM` clause is required (`logs`, `project_logs(...)`, `experiment(...)`, or `dataset(...)`)
* `FROM logs` requires a selected project
* Prefer filtering with `WHERE`; use `HAVING` only after aggregation
* Joins, subqueries, unions, and window functions are not supported
* Use explicit column aliases and type casts for clarity
* Paginate large results with `OFFSET '<cursor_token>'` (not supported with `--async`)

For tips on query performance and common pitfalls, see [SQL best practices](https://docs.braintrust.dev/reference/sql/best-practices.md).

## Flags

| Flag | Env var | Description |
| - | - | - |
| `--non-interactive` | — | Force non-interactive mode |
| `--force-ignore-linter` | `BRAINTRUST_SQL_FORCE_IGNORE_LINTER` | Run the query even when the SQL linter reports failures |

For async query flags, see [Async query flags](#async-query-flags).

## Async queries

> [!NOTE]
>
> Async queries are only available on the [Enterprise plan](https://docs.braintrust.dev/plans-and-limits.md#plans).

On Braintrust-hosted deployments, synchronous queries time out after 30 seconds. For longer queries, pass `--async` to run the query in the background.

Async queries process rows in batches, in the order Braintrust stores them, so they can select and filter rows but can't aggregate, deduplicate, or sort them. `count()`, `sum()`, `GROUP BY`, `SELECT DISTINCT`, `ORDER BY`, and `OFFSET` aren't supported. For runtime, result size, retention, and concurrency limits, see [Limits](https://docs.braintrust.dev/api-reference/query.md#limits).

### Run an async query

With `--async`, the CLI submits the query, polls until it finishes, and prints the results.

Async queries need an object ID in the `FROM` clause. Passing a name, such as `project_logs('project-name')`, fails with a "Missing read access" error. To query a project by name, select it with `-p` and use `FROM logs`. `bt` looks up the project and sends its ID.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
bt sql --async -p project-name \
  "SELECT id, input, output FROM logs WHERE created > now() - interval 1 day"
```

* `--async` needs the query up front, as an argument or through stdin. It doesn't open the interactive prompt.
* The SQL linter doesn't check async queries, so narrow a broad query before you run it.
* `bt` downloads every page of results before printing them. For large exports, [submit without waiting](#submit-without-waiting) and fetch the results in pages with the API.

### Submit without waiting

Pass `--no-wait` to submit the query and exit immediately. The CLI prints the submit response as JSON, including the query's `id`.

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
bt sql --async --no-wait -p project-name \
  "SELECT id, input, output FROM logs WHERE created > now() - interval 1 day"
```

`bt` has no command to resume a submitted query. Use the `id` with the API to [check its status](https://docs.braintrust.dev/api-reference/query.md#check-status), [fetch results](https://docs.braintrust.dev/api-reference/query.md#fetch-results), or [cancel it](https://docs.braintrust.dev/api-reference/query.md#cancel-a-query).

### Async query flags

| Flag | Env var | Default | Description |
| - | - | - | - |
| `--async` | `BRAINTRUST_SQL_ASYNC` | `false` | Run the query through the async SQL API |
| `--no-wait` | `BRAINTRUST_SQL_ASYNC_NO_WAIT` | `false` | Submit the query and print its `id` without waiting for results |
| `--poll-interval <DUR>` | `BRAINTRUST_SQL_ASYNC_POLL_INTERVAL` | `2` | Time between status polls while waiting, in seconds or as a duration such as `30s` |
| `--wait-timeout <DUR>` | `BRAINTRUST_SQL_ASYNC_WAIT_TIMEOUT` | — | Maximum time to wait for completion, for example `10m`, `1h`, or `7d`. Without it, `bt` waits until the query finishes |
| `--async-batch-size <N>` | `BRAINTRUST_SQL_ASYNC_BATCH_SIZE` | `100` | Rows to write per execution batch, from 100 to 100,000 |
| `--async-result-limit <N>` | `BRAINTRUST_SQL_ASYNC_RESULT_LIMIT` | `1000` | Rows to fetch per result page, up to 10,000. `bt` fetches every page |

If `--wait-timeout` expires, `bt` exits with an error that includes the query's `id`. The query keeps running, so you can use the `id` with the API to fetch its results or cancel it.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.