# Bonnard limits

> The limits on queries, exports, charts, dashboards, sign-in and stored records in Bonnard, with the value of each and what happens at the limit.

Updated: 2026-10-15

Each row gives a limit that an agent, a person or a script can reach in Bonnard: its value, and what happens at the
limit.

Verified 15 Oct 2026.

Table: Limits at a glance

| Limit | Value |
| --- | --- |
| [Rows that one query returns](#queries) | 5,000 rows |
| [Query time from an AI client or an API key](#queries) | 30 seconds |
| [Rows in a bulk export](#exports) | 500,000 rows |
| [Charts in one `render_chart` call](#charts-and-dashboards) | 10 |
| [Chart and KPI tiles on one dashboard](#charts-and-dashboards) | 16 |
| [Password sign-ins for one email address](#sign-in) | 10 in 10 minutes |
| [Calls in the Free plan](#billing) | 3,000 for each billing period |

## Queries

These limits apply to each query that an agent or an API key runs through an app, with `sql_query`, `render_chart` or
`render_dashboard`.

Table: Query limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Rows that one query returns | 5,000 rows, unless a bulk export raises it | Bonnard keeps the first 5,000 rows and marks the result partial. The agent reads the warning `Result hit the 5000-row ceiling and is truncated. Narrow with WHERE/GROUP BY, or page with LIMIT/OFFSET. Do not sum or average a truncated result.` |
| Rows in the agent's preview of a `sql_query` result | 250 rows | The agent reads 250 rows and the warning `Showing the first 250 of <n> rows. Page with LIMIT/OFFSET. Do not sum or average a truncated result.` An export of the result holds all the rows. |
| Query time from an AI client or an API key | 30 seconds | The query stops with a timeout error from the database. On DuckDB / MotherDuck, Bonnard stops it with `Query exceeded the 30s time limit.` Narrow the query, for example with a date filter or an aggregate. |
| Bytes billed for one BigQuery query | 200 MB | BigQuery does not run the query, and returns its bytes-billed error. Narrow the query so that it reads less data. |
| Text in one cell of a preview | 2 KB | Bonnard cuts the text and adds `…[truncated; <n> bytes total]`. An export keeps the full value. |
| Size of one reply in Claude Code | 48 KB. Other AI clients have no reply size limit | The agent reads fewer preview rows, with a warning that says `The full set exceeds this response's size budget.` |
| Call intent: the reason that an agent gives in the `intent` argument of a tool | 300 characters | The call fails with `<tool>: invalid arguments.` and does not run. Shorten the intent. The limit applies when **Track call intent** is on in the app's **Settings** tab, which is the default. |

## Exports

An agent exports a result as a CSV, CSV.GZ or PNG file with `create_export_file`, when the app's **Export data** tool is on. For a bulk export, the agent runs `sql_query` with `max_rows` and `capture_only`. The result reference (`resultRef`) that it gets back goes to `create_export_file`.

Table: Export limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Rows in a standard export | 5,000 rows | The export holds the rows that the query returned. |
| Rows in a bulk export: `sql_query` with `max_rows` | 500,000 rows | Bonnard uses 500,000 instead of a larger `max_rows`. The agent reads `max_rows was clamped to this server's bulk ceiling of 500000 rows.` |
| Result data in one export | 8 MB, or 256 MB for a bulk export | Bonnard keeps fewer rows and marks the export partial. Select fewer columns, or split the export. |
| Life of a result reference or an export file | 30 minutes | The export fails, and the agent reads `References last 30 minutes.` The agent runs the query again for a new reference. |

## Charts and dashboards

These limits apply to the charts and dashboards that an agent renders, and to the dashboards that you build in the
console at `app.bonnard.ai`.

Table: Chart and dashboard limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Charts in one `render_chart` call | 10 | The whole call fails with `render_chart: invalid arguments.` Split the charts into calls of 10 or fewer. |
| Rows that one chart or dashboard tile plots | 5,000 rows | The chart plots the first 5,000 rows. An agent also reads the row warning, with a pointer to a bulk export through `sql_query`. |
| Rows of each chart in the agent's reply, set with `preview_rows` | 10 by default, up to 150 | A larger value fails the call with `render_chart: invalid arguments.` |
| Chart and KPI tiles on one dashboard | 16. Text tiles do not count | The save fails. The agent reads `dashboard "<id>": <n> chart/KPI tiles exceeds the 16-tile cap.` In the console, **Chart** and **KPI** are not available after 16 chart and KPI tiles. Split the tiles into two dashboards. |
| Dashboard title | 200 characters | The save fails with `dashboard "<id>": title is required (<=200 chars)`. |
| Dashboard description | 500 characters | The save fails with `dashboard "<id>": description must be a string of <=500 chars`. |
| Dashboard ID | 64 characters: lowercase letters, digits, `-` and `_`. The first character is a letter | The save fails with `invalid dashboard id "<id>"`. |
| Param label | 60 characters | The save fails. Shorten the label. |
| Values in a filter menu | 20 when the menu opens, then 50 at a time, up to the first 550 | Type in the menu to find a value after the first 550. |
| Query time for a dashboard or chart that opens in a browser | 15 seconds | The query stops with a timeout error from the database. The same limit applies to previews in the dashboard editor. |
| Chart data in an AI client | 24 hours after the agent renders the chart | The chart shows "This chart's data has expired; ask for it again." A dashboard also shows **Render again**. |
| Requests to **Draft with AI**, the AI helper in the dashboard editor | 20 an hour for each workspace | The console shows "Over the 20/hour draft-ai limit for this org." The count starts again one hour after the first request. |
| Query time in **Draft with AI** | 15 seconds | The query stops with a timeout error from the database. |

## Databases

Table: Database limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Datasets that one [BigQuery](https://bonnard.ai/docs/connect-data/bigquery.md) service account can read | 100 datasets | Bonnard reads no tables, and shows `bigquery discovery covers at most 100 datasets and this service account can see more`. Grant the service account fewer datasets. |
| Tables in those datasets | 2,000 tables | Bonnard reads no tables, and shows `bigquery discovery covers at most 2000 tables`. Grant the service account fewer datasets. |

## Apps

Table: App limits

| Limit | Value | At the limit |
| --- | --- | --- |
| App name | 80 characters | The name field stops at 80 characters. When you create an app, it also shows `Names can be up to 80 characters.` |

## Sign-in

Bonnard counts sign-in requests by email address, by company domain and by IP address. Domains of public mailbox
services, for example gmail.com, have no domain limit. An HTTP 429 answer has a `Retry-After` header with the seconds
to wait.

Table: Sign-in limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Password sign-ins for one email address | 10 in 10 minutes | HTTP 429 with `Too many attempts. Try again later.` |
| Password sign-ins for one company domain | 100 in 10 minutes | HTTP 429, as for one email address. |
| Sign-ups for one email address | 3 in 1 hour | HTTP 429, as for password sign-in. |
| Sign-ups for one company domain | 50 in 1 hour | HTTP 429, as for password sign-in. |
| Email codes to one console email address | 3 in 10 minutes, and 10 in 24 hours | Bonnard answers as if it sent the code, and sends nothing. Ask again after 10 minutes, or after 24 hours for the second limit. |
| Email codes to one company domain, for the console | 50 in 1 hour | Bonnard answers as if it sent the code, and sends nothing. Ask again after 1 hour. |
| Password reset emails | 3 in 15 minutes for one email address, and 30 in 1 hour for one company domain | Bonnard answers as if it sent the email, and sends nothing. Ask again after the time in the value column. |
| Code entries for one email address | 10 in 15 minutes | HTTP 400 with `That code is invalid or expired.` A correct code also fails. Wait 15 minutes, then ask for a new code. |
| Wrong entries of one email code | 3 | The code becomes invalid. Ask for a new code. |
| Life of an email code | 10 minutes | The code becomes invalid. Ask for a new code. |
| Customer sign-in requests | 5 in 15 minutes for one email address, and 20 in 15 minutes for one IP address | Bonnard sends no code and gives the same answer as for an unknown address. |
| Requests from one IP address | 10 password sign-ins, 5 sign-ups and 5 email code requests in 1 minute | HTTP 429 with a `Retry-After` header. |
| Session in a browser | 30 days | Sign in again. |

## Workspace members

Members are the people in your workspace who sign in to the console.

Table: Workspace member limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Open invitations in one workspace | 100 | Bonnard refuses more invitations. |
| Life of an invitation | 48 hours | The invitation link expires. Send a new invitation. |

## AI client access

An AI client signs in to an app through OAuth, or a script connects with an API key.

Table: Access limits for AI clients and API keys

| Limit | Value | At the limit |
| --- | --- | --- |
| Access token | 15 minutes | A call with an expired token gets HTTP 401 with `The access token has expired.` The AI client uses its refresh token to get a new one. |
| Refresh token | 30 days after sign-in. A refresh does not extend it | Sign in again from the AI client. |
| Client registrations from one IP address: an AI client registers when a person installs an app | 10 in 1 minute | HTTP 429 with a `Retry-After` header. |
| Token and authorisation requests from one IP address | 120 in 1 minute | HTTP 429 with a `Retry-After` header. |
| JSON-RPC requests in one batch | 100 | HTTP 400 with `This server accepts at most 100 JSON-RPC requests in one batch; this request carried <n>.` |
| API keys in one workspace | No limit | None. |
| Life of an API key | No expiry. A key works until an admin revokes it | A revoked key gets HTTP 401 with `The API key is invalid or has been revoked.` |

## Data retention

Bonnard keeps a record of each tool call. These limits apply to the records and to a deleted workspace.

Table: Data retention limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Content of a call record: the SQL, the error, the tool input and the tool output | 90 days | Bonnard deletes the content. The record stays, with its call intent. |
| Call records | 13 months | Bonnard deletes the record. |
| Tool input in a call record | 4,000 characters | Bonnard keeps the first 4,000 characters and adds `...[truncated]`. |
| Tool output in a call record | 16,000 characters | Bonnard keeps the first 16,000 characters and adds `...[truncated]`. |
| A deleted workspace | 14 days | After 14 days, Bonnard deletes the workspace, everything in it and its apps. Until then, you can cancel the deletion in the console. |

## Billing

Tool calls from AI clients and API keys count toward the plan. Use of the console does not count. Extra usage bills the calls past the plan, up to a spend limit if an owner sets one. An owner sets it in **Turn on extra usage**.

Table: Billing limits

| Limit | Value | At the limit |
| --- | --- | --- |
| Calls in the plan, for each billing period | Free: 3,000. Team: 20,000. Scale: 100,000 | If extra usage is off, apps refuse calls. A member's agent reads `This workspace has used all the calls included in its plan for this billing period. An owner needs to turn on extra usage or move to a larger plan before this app can serve more calls.` |
| Spend limit for extra usage | The amount that an owner sets in **Turn on extra usage** | Apps refuse calls until the next billing period, or until an owner raises the limit. |
| Extra usage with no payment method | No calls past the plan | Apps refuse calls past the plan until an owner adds a payment method. |
| Warning before the limit | 80% of the plan's calls, or of the spend limit | The console shows a banner. Calls continue. |
| Grace period after a failed payment | 14 days | Apps serve only the calls in the plan during the grace period. After it, apps refuse calls until an owner updates the payment method. |

When an app refuses calls for billing, your customers' agents get an error that names no plan, price or limit.
