Skip to main content
The /btql endpoint runs SQL over your logs, experiments, and datasets and returns the rows in the response. If a query times out, try narrowing it first (see SQL best practices). For large exports, or a well-scoped query that still times out, use an async query.
You can also run SQL queries interactively on the Query page, or with the bt sql CLI command.

Base URL

The base URL depends on your organization’s data plane region: Authenticate every request with your API key in the Authorization header. The examples below use the US base URL.

Query data

Send a POST /btql request with a SQL query in the body. Braintrust runs the query synchronously and returns the results in a single response. Example
Request Send a JSON request body with these fields:
For correct day boundaries, set tz_offset to match your timezone. For example, use 480 for US Pacific Standard Time.
Response The response format depends on fmt:
  • json (default): an object with a data array of row objects. Each row contains the columns your query selects, or every column for SELECT *.
  • jsonl: one row per line, for streaming.
  • parquet: a binary column format, suited to large exports.
JSON
To fetch more rows than fit in one response, see Pagination.

Pagination

Each response returns one page of rows. To fetch every page:
  1. Run the query, and read the cursor from the x-bt-cursor response header.
  2. Resend the same query with the cursor:
    • SQL string: append OFFSET '<cursor>'. See Syntax styles.
    • Structured query object: set its cursor field.
  3. Repeat until a response has no cursor.
On Braintrust-hosted deployments, large responses redirect to a stored copy of the results. Follow the redirect (for example, with curl -L), and read the cursor from the x-amz-meta-bt_cursor header instead.
Example This example fetches every row of a dataset with SELECT * FROM dataset(...).
If you use the Python or TypeScript SDK to read a dataset or experiment, you don’t need this code. Iterate over the dataset or experiment, and the SDK handles the cursor for you.

Timeouts

On Braintrust-hosted deployments, synchronous queries time out after 30 seconds. That’s enough for interactive queries, dashboards, and most routine reads. If a query times out, try optimizing it first. For large exports, or a well-scoped query that still times out, use an async query.

Examples

This example exports up to 100 traces from the last 7 days of a project’s logs to export.json and export.parquet. You can export logs, experiments, or datasets to JSON or Parquet by setting fmt.Each request returns one page of results. Without a LIMIT, a project_logs() query returns the first 10 rows, or 10 traces with shape => 'traces', and other sources return the first 1,000. To export more, follow the x-bt-cursor header as described in Pagination.
A large export takes many synchronous requests, and each one times out after 30 seconds. To export many thousands of rows, submit the query to the async query endpoints instead.
This example checks how much of an experiment has been reviewed. It returns a single row with two counts: reviewed, the number of rows with a value in your review score column, and total, the number of root spans, one per test case. Replace response quality with the name of your review score. You can adapt the query to check other experiment metrics.
This example measures how long a particular step takes across traces that share a metadata value. It retrieves up to 10 traces whose metadata matches, including all of their child spans, then keeps the spans with a given name and prints each span’s duration and the average, in seconds. Replace your-org-name with the metadata value to match, and your-span-name with the name of the span to measure.

Async queries

Async queries are only available on the Enterprise plan.
Async queries run in the background, so they aren’t limited by the synchronous timeout. Braintrust stores the results, and you fetch them in pages. Use an async query for:
  • A large export or backfill that returns many thousands of rows.
  • A well-scoped query that still times out as a synchronous request.
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. Async queries address request timeouts and long runtimes. They aren’t a substitute for query optimization, so narrow a broad query before running it asynchronously. See SQL best practices.

Limits

Braintrust-hosted
  • Runtime: 1 hour of cumulative runtime.
  • Result size: 10 GiB.
  • Retention: 7 days. Fetch the results within that window.
Self-hosted
  • Requirements: data plane v2.3.0 or later (Helm chart 6.4.0 or later). If you deploy with the AWS Terraform module, use v6.0.0 or later and set enable_ecs_api = true. With the default setting, the async endpoints aren’t reachable.
  • Runtime: 1 hour of cumulative runtime by default.
  • Result size: 200 GiB by default on data plane v2.17.0 and later (Helm chart 6.20.0 or Terraform module v6.9.0 and later), and 10 GiB on earlier versions.
A query that exceeds the runtime or result size limit stops and reports a failed status with an explanatory error.

Submit a query

POST /btql/async submits a query and returns its id, with a status of queued. Example
You can also submit async queries with the bt sql --async CLI command. By default, it waits for the query to finish and prints the results. Pass --no-wait to print the submit response, including the query’s id, and exit instead.
Request Send a JSON request body with these fields: Unlike POST /btql, the async endpoint ignores fmt, lint_mode, version, and tz_offset, and query must be a string rather than a structured query object. Response
JSON
The response includes:

Check status

GET /btql/async/:id returns the query’s progress. Poll it until the status is succeeded, failed, or canceled. Example
Request No request body. Pass the query’s id in the path. Response
JSON
The response includes:

Fetch results

GET /btql/async/:id/result returns a page of results as rows, along with a cursor for the next page. Results are complete only after status is succeeded. Before then, this endpoint returns whatever rows are available so far. Example
Request Pass the query’s id in the path, and these URL query parameters: Response
JSON
The response includes: A page can contain fewer than limit rows, so don’t treat a short page as the last one.

Cancel a query

POST /btql/async/:id/cancel cancels a queued or running query. Canceling a query that already finished discards its results. Only the user who submitted the query, or a user with update permission on the organization, can cancel it. Example
Request No request body. Pass the query’s id in the path. Response
JSON
The response includes:

Next steps

  • Learn the SQL syntax for querying logs, experiments, and datasets.
  • Follow SQL best practices to keep queries fast and within limits.
  • Run queries with the bt sql CLI command.