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

# SQL API

> Execute SQL queries over HTTP using the libSQL remote protocol

> [!WARNING]
>
> The SQL API is currently in beta. APIs and behavior may change.

The SQL API allows you to execute SQL queries directly over HTTP without using an SDK. This is useful for environments where an SDK isn't available, or when you need to make simple queries from any HTTP client.

Bunny Database uses the [libSQL remote protocol](https://github.com/tursodatabase/libsql/blob/main/docs/HRANA_3_SPEC.md#hrana-over-http) (Hrana over HTTP) for its SQL API.

## Quickstart

**1. Get your Database URL**

Your Database URL can be found in the Dashboard under **Edge Platform > Database > \[Select Database] > Access**.

The HTTP endpoint follows this format:

```
https://[your-database-id].lite.bunnydb.net/v2/pipeline
```

> [!NOTE]
>
> Note the `/v2/pipeline` path — this is the endpoint that accepts SQL requests.

**2. Get your Access Token**

You'll need an access token to authenticate requests. Generate one from the same Access page in the Dashboard, or see [Database Access](/docs/database/connect/authorization) for details.

**3. Execute a query**

Send a POST request to the pipeline endpoint with your SQL statement:

```bash cURL theme={null}
curl -X POST https://[your-database-id].lite.bunnydb.net/v2/pipeline \
  -H "Authorization: Bearer your-access-token" \
  -H "Content-Type: application/json" \
  -d '{
    "requests": [
      { "type": "execute", "stmt": { "sql": "SELECT * FROM users" } },
      { "type": "close" }
    ]
  }'
```

```ts JavaScript theme={null}
const url = "https://[your-database-id].lite.bunnydb.net/v2/pipeline";
const authToken = "your-access-token";

const response = await fetch(url, {
  method: "POST",
  headers: {
    Authorization: `Bearer ${authToken}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    requests: [
      { type: "execute", stmt: { sql: "SELECT * FROM users" } },
      { type: "close" },
    ],
  }),
});

const data = await response.json();
console.log(data);
```

```python Python theme={null}
import requests

url = "https://[your-database-id].lite.bunnydb.net/v2/pipeline"
auth_token = "your-access-token"

response = requests.post(
    url,
    headers={
        "Authorization": f"Bearer {auth_token}",
        "Content-Type": "application/json",
    },
    json={
        "requests": [
            {"type": "execute", "stmt": {"sql": "SELECT * FROM users"}},
            {"type": "close"},
        ]
    },
)

print(response.json())
```

## Request format

Each request to `/v2/pipeline` contains an array of requests to execute. A typical request includes an `execute` statement followed by a `close`:

```json theme={null}
{
  "requests": [
    { "type": "execute", "stmt": { "sql": "SELECT * FROM users" } },
    { "type": "close" }
  ]
}
```

## Response format

The response contains a `results` array with the outcome of each request:

```json theme={null}
{
  "baton": null,
  "base_url": null,
  "results": [
    {
      "type": "ok",
      "response": {
        "type": "execute",
        "result": {
          "cols": [
            { "name": "id", "decltype": "INTEGER" },
            { "name": "name", "decltype": "TEXT" }
          ],
          "rows": [
            [
              { "type": "integer", "value": "1" },
              { "type": "text", "value": "Kit" }
            ]
          ],
          "affected_row_count": 0,
          "last_insert_rowid": null,
          "replication_index": "1"
        }
      }
    },
    {
      "type": "ok",
      "response": {
        "type": "close"
      }
    }
  ]
}
```

## Parameter binding

Use parameter binding to safely pass values to your queries. This helps prevent SQL injection and handles proper escaping.

### Positional parameters

Use `?` placeholders and provide values in the `args` array:

```json theme={null}
{
  "requests": [
    {
      "type": "execute",
      "stmt": {
        "sql": "SELECT * FROM users WHERE id = ?",
        "args": [{ "type": "integer", "value": "1" }]
      }
    },
    { "type": "close" }
  ]
}
```

### Named parameters

Use `:name`, `$name`, or `@name` placeholders with `named_args`:

```json theme={null}
{
  "requests": [
    {
      "type": "execute",
      "stmt": {
        "sql": "SELECT * FROM users WHERE name = :name",
        "named_args": [
          {
            "name": "name",
            "value": { "type": "text", "value": "Kit" }
          }
        ]
      }
    },
    { "type": "close" }
  ]
}
```

## Value types

Each value is a JSON object with a `type` field. The shape of the object depends on the type:

| Type | Description | JSON shape |
| - | - | - |
| `null` | NULL value | `{ "type": "null" }` |
| `integer` | 64-bit signed integer | `{ "type": "integer", "value": "42" }` |
| `float` | 64-bit floating point | `{ "type": "float", "value": 3.14 }` |
| `text` | UTF-8 string | `{ "type": "text", "value": "Kit" }` |
| `blob` | Binary data (base64 encoded) | `{ "type": "blob", "base64": "AAH/IGJpbmFyeQ" }` |

The server rejects values that do not match these shapes:

* An `integer` value must be a JSON string, not a number. This avoids precision loss, since some JSON implementations treat all numbers as 64-bit floats.
* A `float` value must be a JSON number. Sending it as a string returns a `400` error.
* A `blob` carries its data in a `base64` field, not in `value`. The server accepts base64 with or without padding.

> [!NOTE]
>
> Blobs in responses are encoded as base64 without padding. If your base64
> decoder is strict, add the missing `=` padding before decoding.

## Multiple statements

You can execute multiple statements in a single request:

```json theme={null}
{
  "requests": [
    {
      "type": "execute",
      "stmt": { "sql": "INSERT INTO users (name) VALUES ('Kit')" }
    },
    { "type": "execute", "stmt": { "sql": "SELECT * FROM users" } },
    { "type": "close" }
  ]
}
```

Each statement executes in order, and the results array contains the response for each.

## Interactive sessions

For most use cases, executing statements with a `close` request in a single HTTP call is sufficient. The API also supports interactive sessions using a `baton`, a token returned in responses that allows you to maintain state across multiple HTTP requests. This is useful for advanced scenarios like long-running transactions that span multiple roundtrips.

> [!NOTE]
>
> If you have a use case that requires interactive sessions with batons,
> [contact us](https://bunny.net/contact) to discuss your requirements.


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