> ## 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.

# API Reference

> Complete API reference for the Braintrust API

The Braintrust API allows you to interact with all aspects of the Braintrust platform programmatically. You can use it to:

* Create and manage projects, experiments, and datasets
* Log traces and metrics
* Manage prompts, tools, and scorers
* Configure access control and permissions
* Retrieve and analyze results

The API is defined by an OpenAPI specification published at [braintrust-openapi](https://github.com/braintrustdata/braintrust-openapi) on GitHub.

## Base URL

The base URL depends on your organization's [data plane region](/docs/admin/organizations#data-plane-region):

| Region      | Base URL                        |
| ----------- | ------------------------------- |
| US          | `https://api.braintrust.dev`    |
| EU          | `https://api-eu.braintrust.dev` |
| Self-hosted | Your custom data plane URL      |

You can find your API URL in **<Icon icon="settings-2" /> Settings** > [**<Icon icon="lock" /> Data plane**](https://www.braintrust.dev/app/~/configuration/org/api-url).

## Authentication

Authenticate requests with your API key in the Authorization header:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
curl https://api.braintrust.dev/v1/project \
  -H "Authorization: Bearer $BRAINTRUST_API_KEY"
```

Create API keys in [Settings > API keys](https://www.braintrust.dev/app/settings?subroute=api-keys).

## SDKs

While you can call the API directly, we recommend using one of our official SDKs:

<CardGroup cols={2}>
  <Card title="TypeScript SDK" icon="js" href="/docs/sdks/typescript/quickstart">
    Official TypeScript/JavaScript SDK
  </Card>

  <Card title="Python SDK" icon="python" href="/docs/sdks/python/quickstart">
    Official Python SDK
  </Card>

  <Card title="Go SDK" icon="golang" href="/docs/sdks/go/quickstart">
    Official Go SDK
  </Card>

  <Card title="Ruby SDK" icon="gem" href="/docs/sdks/ruby/quickstart">
    Official Ruby SDK
  </Card>

  <Card title="Java SDK" icon="java" href="/docs/sdks/java/quickstart">
    Official Java SDK
  </Card>

  <Card title="C# SDK" icon="microsoft" href="/docs/sdks/csharp/quickstart">
    Official C# SDK
  </Card>

  <Card title="Kotlin SDK" icon="code" href="https://github.com/braintrustdata/braintrust-kotlin">
    Official Kotlin SDK
  </Card>
</CardGroup>

## API resources

The API is organized around REST principles. Each resource has predictable URLs and uses HTTP response codes to indicate API errors.

**Project resources**

* **Projects**: Organize your AI features and experiments
* **Experiments**: Run and track evaluation experiments
* **Datasets**: Manage test data for evaluations
* **Logs**: Store and query production traces
* **Prompts**: Version control your prompts
* **Functions**: Manage tools, scorers, and workflows
* **Evals**: Configure and run evaluations
* **Scores**: Define custom scoring functions
* **Tags**: Organize and filter project resources
* **Automations**: Configure automated workflows
* **Views**: Create and manage custom data views

**Organization resources**

* **Organizations**: Manage your organization settings
* **Users**: Manage team members
* **Groups**: Organize users into teams
* **Roles**: Define permission levels
* **ACLs**: Configure fine-grained access control
* **API keys**: Manage authentication credentials
* **Service tokens**: Generate service-level authentication tokens

**Configuration resources**

* **AI secrets**: Securely store API keys and credentials
* **Environment variables**: Manage environment-specific configuration
* **MCP servers**: Configure Model Context Protocol servers
* **Proxy**: Configure proxy settings for API requests

## Response format

All API responses are returned in JSON format. Successful responses will have a `2xx` status code, while errors will return `4xx` or `5xx` status codes with error details.

## Rate limits

The API uses rate limiting to ensure fair usage. Rate limits are applied per organization and endpoint. If you exceed the rate limit, you'll receive a `429 Too Many Requests` response.

## Query data

Query your logs, experiments, and datasets with SQL through the `/btql` endpoint. For a full reference, see [Query by SQL](/docs/api-reference/query).

### Filter experiments by metadata

Filter experiments by metadata field equality using the `metadata` query parameter on [`GET /v1/experiment`](/docs/api-reference/experiments/get-experiment). Pass a JSON-serialized object to match experiments where all specified fields are equal, including nested paths:

<CodeGroup dropdown>
  ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import json
  import os
  import requests

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

  response = requests.get(
      f"{API_URL}/experiment",
      headers=headers,
      params=dict(
          project_id="your-project-id",
          metadata=json.dumps({"env": "production", "model": {"name": "gpt-5-mini"}}),
      ),
  )

  experiments = response.json().get("objects", [])
  for experiment in experiments:
      print(experiment["id"], experiment["name"])
  ```

  ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  const API_URL = "https://api.braintrust.dev/v1";
  const headers = {
    Authorization: `Bearer ${process.env.BRAINTRUST_API_KEY}`,
  };

  const params = new URLSearchParams({
    project_id: "your-project-id",
    metadata: JSON.stringify({ env: "production", model: { name: "gpt-5-mini" } }),
  });

  const response = await fetch(`${API_URL}/experiment?${params}`, { headers });
  const { objects: experiments } = await response.json();
  for (const experiment of experiments) {
    console.log(experiment.id, experiment.name);
  }
  ```
</CodeGroup>

## Invoke functions

Call prompts, tools, or scorers via the `/v1/function` endpoint:

```bash theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
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**

```json theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
{
  "output": "Summarized text here...",
  "metadata": {
    "model": "claude-3-5-sonnet-latest",
    "tokens": 150,
    "latency": 0.85
  }
}
```

## Write and manage data

### Run experiments

Create and run experiments programmatically:

<CodeGroup dropdown>
  ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  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}
              }]
          }
      )
  ```
</CodeGroup>

### Log programmatically

Insert logs via the API:

<CodeGroup dropdown>
  ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  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"}
          }]
      }
  )
  ```
</CodeGroup>

### Delete logs

Mark logs for deletion by setting `_object_delete`:

<CodeGroup dropdown>
  ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  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}
  )
  ```
</CodeGroup>

## Impersonate users

User impersonation lets a privileged user perform an operation on behalf of another user, using the impersonated user's identity and permissions. For example, a proxy service can forward requests from individual users to Braintrust without each user supplying their own credentials: the service authenticates with its own key and names the user to impersonate, and Braintrust runs the operation as that user.

Braintrust requires that:

* The requesting user has the `Owner` role over all organizations the impersonated user belongs to.
* The impersonated user belongs to at least one organization.

Together, these requirements guarantee the requesting user has at least the set of permissions that the impersonated user has.

To impersonate a user, set the `x-bt-impersonate-user` header to the ID or email of the user you want to impersonate. The examples below show how to configure ACLs and make a request using user impersonation:

<CodeGroup dropdown>
  ```typescript theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  // If you're self-hosting Braintrust, then use your stack's Universal API URL, e.g.
  //   https://dfwhllz61x709.cloudfront.net
  export const BRAINTRUST_API_URL = "https://api.braintrust.dev";
  export const API_KEY = process.env.BRAINTRUST_API_KEY;

  async function getOwnerRoleId() {
    const roleResp = await fetch(
      `${BRAINTRUST_API_URL}/v1/role?${new URLSearchParams({ role_name: "Owner" })}`,
      {
        method: "GET",
        headers: {
          Authorization: `Bearer ${API_KEY}`,
        },
      },
    );
    if (!roleResp.ok) {
      throw new Error(await roleResp.text());
    }
    const roles = await roleResp.json();
    return roles.objects[0].id;
  }

  async function getUserOrgInfo(orgName: string): Promise<{
    user_id: string;
    org_id: string;
  }> {
    const meResp = await fetch(`${BRAINTRUST_API_URL}/api/self/me`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
      },
    });
    if (!meResp.ok) {
      throw new Error(await meResp.text());
    }
    const meInfo = await meResp.json();
    const orgInfo = meInfo.organizations.find(
      (x: { name: string }) => x.name === orgName,
    );
    if (!orgInfo) {
      throw new Error(`No organization found with name ${orgName}`);
    }
    return { user_id: meInfo.id, org_id: orgInfo.id };
  }

  async function grantOwnershipRole(orgName: string) {
    const ownerRoleId = await getOwnerRoleId();
    const { user_id, org_id } = await getUserOrgInfo(orgName);

    // Grant an 'Owner' ACL to the requesting user on the organization. Granting
    // this ACL requires the user to have `create_acls` permission on the org, which
    // means they must already be an owner of the org indirectly.
    const aclResp = await fetch(`${BRAINTRUST_API_URL}/v1/acl`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        object_type: "organization",
        object_id: org_id,
        user_id,
        role_id: ownerRoleId,
      }),
    });
    if (!aclResp.ok) {
      throw new Error(await aclResp.text());
    }
  }

  async function main() {
    if (!process.env.ORG_NAME || !process.env.USER_EMAIL) {
      throw new Error("Must specify ORG_NAME and USER_EMAIL");
    }

    // This only needs to be done once.
    await grantOwnershipRole(process.env.ORG_NAME);

    // This will only succeed if the user being impersonated has permissions to
    // create a project within the org.
    const projectResp = await fetch(`${BRAINTRUST_API_URL}/v1/project`, {
      method: "POST",
      headers: {
        Authorization: `Bearer ${API_KEY}`,
        "Content-Type": "application/json",
        "x-bt-impersonate-user": process.env.USER_EMAIL,
      },
      body: JSON.stringify({
        name: "my-project",
        org_name: process.env.ORG_NAME,
      }),
    });
    if (!projectResp.ok) {
      throw new Error(await projectResp.text());
    }
    console.log(await projectResp.json());
  }

  main();
  ```

  ```python theme={"theme":{"light":"github-light","dark":"github-dark-dimmed"}}
  import os
  import requests

  # If you're self-hosting Braintrust, then use your stack's Universal API URL, e.g.
  # https://dfwhllz61x709.cloudfront.net
  BRAINTRUST_API_URL = "https://api.braintrust.dev"
  API_KEY = os.environ["BRAINTRUST_API_KEY"]

  def get_owner_role_id():
      resp = requests.get(
          f"{BRAINTRUST_API_URL}/v1/role",
          headers={"Authorization": f"Bearer {API_KEY}"},
          params=dict(role_name="Owner"),
      )
      resp.raise_for_status()
      return resp.json()["objects"][0]["id"]

  def get_user_org_info(org_name):
      resp = requests.post(
          f"{BRAINTRUST_API_URL}/api/self/me",
          headers={"Authorization": f"Bearer {API_KEY}"},
      )
      resp.raise_for_status()
      me_info = resp.json()
      org_info = [x for x in me_info["organizations"] if x["name"] == org_name]
      if not org_info:
          raise Exception(f"No organization found with name {org_name}")
      return dict(user_id=me_info["id"], org_id=org_info[0]["id"])

  def grant_ownership_role(org_name):
      owner_role_id = get_owner_role_id()
      user_org_info = get_user_org_info(org_name)

      # Grant an 'Owner' ACL to the requesting user on the organization. Granting
      # this ACL requires the user to have `create_acls` permission on the org,
      # which means they must already be an owner of the org indirectly.
      resp = requests.post(
          f"{BRAINTRUST_API_URL}/v1/acl",
          headers={"Authorization": f"Bearer {API_KEY}"},
          json=dict(
              object_type="organization",
              object_id=user_org_info["org_id"],
              user_id=user_org_info["user_id"],
              role_id=owner_role_id,
          ),
      )
      resp.raise_for_status()

  def main():
      # This only needs to be done once.
      grant_ownership_role(os.environ["ORG_NAME"])

      # This will only succeed if the user being impersonated has permissions to
      # create a project within the org.
      resp = requests.post(
          f"{BRAINTRUST_API_URL}/v1/project",
          headers={
              "Authorization": f"Bearer {API_KEY}",
              "x-bt-impersonate-user": os.environ["USER_EMAIL"],
          },
          json=dict(
              name="my-project",
              org_name=os.environ["ORG_NAME"],
          ),
      )
      resp.raise_for_status()
      print(resp.json())

  if __name__ == "__main__":
      main()
  ```
</CodeGroup>

## Next steps

* Explore the [complete API reference](/docs/api-reference) for all available endpoints
* Learn about [SQL querying](/docs/reference/sql) to analyze your data
* Review [system limits](/docs/plans-and-limits) for API usage constraints
* Check out the [Python SDK](/docs/sdks/python/versions/latest) or [TypeScript SDK](/docs/sdks/typescript/versions/latest) documentation

## Support

Need help with the API?

* Join our [Discord community](https://discord.gg/6G8s47F44X)
* Email us at [support@braintrust.dev](mailto:support@braintrust.dev)
