Skip to main content
The Braintrust Data API provides programmatic access to all platform features through a REST interface. Use it to invoke functions, query logs, manage datasets, run experiments, and integrate Braintrust into your workflows.

Authentication

Authenticate requests with your API key in the Authorization header:
curl https://api.braintrust.dev/v1/project \
  -H "Authorization: Bearer $BRAINTRUST_API_KEY"
Create API keys in Settings > API keys.

Invoke functions

Call prompts, tools, or scorers via the /v1/function endpoint:
curl https://api.braintrust.dev/v1/function \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $BRAINTRUST_API_KEY" \
  -d '{
    "project_name": "My Project",
    "slug": "summarizer",
    "input": {
      "text": "Long text to summarize..."
    }
  }'

Parameters

  • project_name or project_id: Project containing the function
  • slug: Function slug
  • input: Function input parameters
  • version (optional): Pin to a specific version
  • environment (optional): Use environment-specific version
  • stream (optional): Enable streaming responses

Response

{
  "output": "Summarized text here...",
  "metadata": {
    "model": "claude-3-5-sonnet-latest",
    "tokens": 150,
    "latency": 0.85
  }
}

Query logs and experiments

Use the /btql endpoint to query data with SQL syntax:
import os
import requests

API_URL = "https://api.braintrust.dev/"
headers = {"Authorization": "Bearer " + os.environ["BRAINTRUST_API_KEY"]}

query = """
SELECT id, input, output, scores
FROM project_logs('your-project-id', shape => 'traces')
WHERE scores.accuracy > 0.8
LIMIT 100
"""

response = requests.post(
    f"{API_URL}/btql",
    headers=headers,
    json={"query": query, "fmt": "json"},
).json()

for row in response["data"]:
    print(row)

SQL syntax

Query data using standard SQL syntax or BTQL’s alternative pipe-delimited syntax:
SELECT input, output, scores.accuracy
FROM project_logs('project-id', shape => 'traces')
WHERE metadata.environment = 'production'
  AND scores.accuracy < 0.5
ORDER BY created DESC
LIMIT 100
See the SQL reference for complete syntax details.

Export data

Export logs, experiments, or datasets to JSON or Parquet:
import os
import requests

API_URL = "https://api.braintrust.dev/"
headers = {"Authorization": "Bearer " + os.environ["BRAINTRUST_API_KEY"]}

query = """
SELECT *
FROM project_logs('your-project-id', shape => 'traces')
WHERE created >= now() - interval '7 days'
"""

# JSON format
response = requests.post(
    f"{API_URL}/btql",
    headers=headers,
    json={"query": query, "fmt": "json"},
).json()

# Parquet format (returns binary data)
response = requests.post(
    f"{API_URL}/btql",
    headers=headers,
    json={"query": query, "fmt": "parquet"},
)
with open("export.parquet", "wb") as f:
    f.write(response.content)

Paginate large datasets

For large datasets, paginate using cursors:
const API_URL = "https://api.braintrust.dev/";
const headers = {
  Authorization: `Bearer ${process.env.BRAINTRUST_API_KEY}`,
};

async function* paginateDataset(projectName: string, datasetName: string) {
  // Get dataset ID
  const dsResp = await fetch(
    `${API_URL}/v1/dataset?project_name=${projectName}&dataset_name=${datasetName}`,
    { headers }
  );
  const ds = await dsResp.json();
  const datasetId = ds.objects[0].id;

  let cursor = null;
  while (true) {
    const response = await fetch(`${API_URL}/btql`, {
      method: "POST",
      headers,
      body: JSON.stringify({
        query: {
          from: {
            op: "function",
            name: { op: "ident", name: ["dataset"] },
            args: [{ op: "literal", value: datasetId }],
          },
          select: [{ op: "star" }],
          limit: 100,
          cursor,
        },
        fmt: "jsonl",
      }),
    });

    cursor =
      response.headers.get("x-bt-cursor") ||
      response.headers.get("x-amz-meta-bt_cursor");

    const text = await response.text();
    const rows = text.split("\n").filter((r) => r.trim());

    if (rows.length === 0) break;

    for (const row of rows) {
      yield JSON.parse(row);
    }
  }
}

// Usage
for await (const row of paginateDataset("My Project", "My Dataset")) {
  console.log(row);
}

Run experiments

Create and run experiments programmatically:
import os
from uuid import uuid4
import requests

API_URL = "https://api.braintrust.dev/v1"
headers = {"Authorization": "Bearer " + os.environ["BRAINTRUST_API_KEY"]}

# Create a project
project = requests.post(
    f"{API_URL}/project",
    headers=headers,
    json={"name": "My Project"}
).json()

# Create an experiment
experiment = requests.post(
    f"{API_URL}/experiment",
    headers=headers,
    json={"name": "Test Run", "project_id": project["id"]}
).json()

# Insert experiment results
for i in range(10):
    requests.post(
        f"{API_URL}/experiment/{experiment['id']}/insert",
        headers=headers,
        json={
            "events": [{
                "id": uuid4().hex,
                "input": {"question": f"Test {i}"},
                "output": f"Answer {i}",
                "scores": {"accuracy": 0.9}
            }]
        }
    )

Log programmatically

Insert logs via the API:
import os
from uuid import uuid4
import requests

API_URL = "https://api.braintrust.dev/v1"
headers = {"Authorization": "Bearer " + os.environ["BRAINTRUST_API_KEY"]}

# Get or create project
project = requests.post(
    f"{API_URL}/project",
    headers=headers,
    json={"name": "My Project"}
).json()

# Insert log event
requests.post(
    f"{API_URL}/project_logs/{project['id']}/insert",
    headers=headers,
    json={
        "events": [{
            "id": uuid4().hex,
            "input": {"question": "What is 2+2?"},
            "output": "4",
            "scores": {"accuracy": 1.0},
            "metadata": {"environment": "production"}
        }]
    }
)

Delete logs

Mark logs for deletion by setting _object_delete:
import os
import requests

API_URL = "https://api.braintrust.dev/"
headers = {"Authorization": "Bearer " + os.environ["BRAINTRUST_API_KEY"]}

# Find logs to delete
query = """
SELECT id
FROM project_logs('project-id', shape => 'traces')
WHERE metadata.user_id = 'test-user'
"""

response = requests.post(
    f"{API_URL}/btql",
    headers=headers,
    json={"query": query}
).json()

ids = [row["id"] for row in response["data"]]

# Delete logs
delete_events = [{"id": id, "_object_delete": True} for id in ids]
requests.post(
    f"{API_URL}/v1/project_logs/project-id/insert",
    headers=headers,
    json={"events": delete_events}
)

Impersonate users

Perform operations on behalf of other users with the x-bt-impersonate-user header:
curl https://api.braintrust.dev/v1/project \
  -H "Authorization: Bearer $BRAINTRUST_API_KEY" \
  -H "x-bt-impersonate-user: [email protected]" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "User Project",
    "org_name": "My Organization"
  }'
Impersonation requires the requesting user to have Owner role over all organizations the impersonated user belongs to.

Use with Postman

Import the OpenAPI spec into Postman for interactive API exploration:
https://raw.githubusercontent.com/braintrustdata/braintrust-openapi/main/openapi/spec.json
Postman

API reference

See the complete API reference for all available endpoints, parameters, and response formats.

Next steps