Errors and limits
The REST API's error shapes and codes, which errors to retry, rate limits and their headers, status codes, pagination, and CORS.
On this page
This page is the reference for what the REST API sends back when something goes
wrong, and for the limits that apply to every integration. Use it to decide,
from the status and code alone, whether to retry, fix the request, or stop.
Error shape#
Most errors share one envelope:
{
"statusCode": 403,
"code": "forbidden_by_agent_allowlist",
"error": "forbidden",
"message": "This API key is not permitted to access the specified agent."
}statusCoderepeats the HTTP status.codeis the machine-readable cause, when there is one. Branch on it.errornames the status class:bad_request(400),unauthorized(401),forbidden(403),not_found(404),conflict(409) ortoo_many_requests(429).messageis written for a person. Don't parse it.
A body that fails schema validation answers 400 with
"code": "FST_ERR_VALIDATION" and a message naming the invalid field.
A server error hides its cause:
{
"statusCode": 500,
"error": "Internal Server Error",
"message": "An internal error occurred. Please try again later."
}Other shapes#
A few responses use a route-specific body with no statusCode or code. The
specific cause is in error instead:
| Where | Body |
|---|---|
POST /v1/sessions and /v1/sessions/refresh, 404 | { "error": "agent_not_found" | "thread_not_found", "message" } |
Agent and assistant create and update, 400 | { "error": "<cause>", "message" }, for example agent_limit_reached, system_prompt_too_long or provider_type_mismatch |
Chat and run routes, 422 | { "error": "unprocessable_entity", "message" } |
Read code when it is present and fall back to error.
Status codes#
| Status | Meaning | Retry? |
|---|---|---|
400 | The request is invalid | No. Fix the request |
401 | The credential is missing, invalid or expired | Only pass_expired, once, after renewing. See authentication errors |
403 | The credential isn't allowed to do this | No |
404 | The resource doesn't exist, or isn't visible to your organization | No |
409 | The request conflicts with the resource's current state | No, until the state changes |
422 | The request is valid but this agent can't process it. See Chat | No |
429 | A rate limit or quota was reached | Yes, after Retry-After seconds, unless code is a lifetime cap |
500 and above | Something failed on Runbear's side | Yes, with backoff |
A 404 for a thread is identical whether the thread doesn't exist or belongs to
another organization.
403 codes#
Every 403 is terminal: retrying, or renewing the credential, won't change the
answer.
code | Cause |
|---|---|
forbidden_by_key_role | The API key lacks the capability this operation requires |
forbidden_by_agent_allowlist | The agent is outside the API key's allowlist |
forbidden_org_endpoint_for_scoped_key | An API key with an agent allowlist called an organization-level endpoint |
forbidden_for_session_pass | A Web SDK session pass called a route it can't use |
forbidden_agent_not_in_pass | A session pass named an agent other than its own |
forbidden_trace_access_for_hipaa_agent | The agent uses HIPAA data handling, which disables trace access |
forbidden_ai_gateway_not_permitted | The organization doesn't have the AI Gateway feature |
forbidden_google_drive_sa | The key's user may not use this Google Drive service account |
forbidden_service_account_manage | The key's user may not manage this Google Drive service account |
See Authentication and API keys for how
the first three are decided. Stream errors, which arrive inside a 200, are
listed on Streaming.
Rate limits#
| Limit | Counted per | Applies to |
|---|---|---|
| 600 requests per 60 seconds | Organization and agent | POST /v1/threads, POST /v1/threads/{threadId}/runs and /runs/stream, POST /v1/chat/completions, POST /v1/chat/suggestions, POST /v1/files/upload |
| 600 requests per 60 seconds | Organization and agent | Creating a Web SDK session, POST /v1/sessions |
| 600 requests per 60 seconds | Organization | Renewing a Web SDK session, POST /v1/sessions/refresh |
| 60 requests per 60 seconds | Web SDK session | Every request made with a session pass |
| 100 turns | Web SDK session, for its whole life | Runs made with a session pass |
| 240 renewals | Web SDK session, for its whole life | POST /v1/sessions/refresh |
| 30 requests per minute | Organization, per API server | GET /v1/agents/{id}/traces and GET /v1/agents/{id}/messages/{messageId}/trace, together |
| 20 requests per minute | Organization, per API server | GET /v1/agents/{id}/traces/export |
- The chat limit is shared by everyone who talks to the agent through your organization, including every visitor of a Web SDK widget for that agent. Contact support@runbear.io before a high-traffic launch.
- A run made with a Web SDK session pass counts against both the chat limit and the session's own limits. See Web SDK session limits for what the widget does when one is reached.
- The trace limits are counted on each API server separately, so the budget you observe can be higher than the number above. Pace yourself by the headers rather than by a fixed count.
- Routes not listed here have no request limit of their own. API-key
validation can still be throttled under heavy load; that answers
429withRetry-Afterlike any other limit. Until a pending API fix ships, a throttled validation answers401api_key_invalidinstead, so a sudden401for a key that worked moments ago is worth one delayed retry.
Rate-limit headers#
Responses from rate-limited routes carry these headers on every response, successful or not:
| Header | Value |
|---|---|
RateLimit-Limit | The size of the budget |
RateLimit-Remaining | What is left of it |
RateLimit-Reset | Seconds until it resets |
Retry-After | On a 429 only: seconds to wait before retrying, at most 3600 |
429 responses#
{
"statusCode": 429,
"code": "rate_limited",
"error": "too_many_requests",
"message": "Too many requests. Retry after the window resets."
}code | Meaning | What to do |
|---|---|---|
rate_limited | A per-window limit was reached | Wait Retry-After seconds and retry |
session_turn_cap_exhausted | The Web SDK session used all its turns | Start a new session |
session_refresh_cap_exhausted | The Web SDK session can't be renewed again | Start a new session |
Trace 429s carry no code; wait Retry-After seconds. Treat any other 429
the same way.
Success status codes#
Most successful calls answer 200. These differ:
| Operation | Status |
|---|---|
POST /v1/threads | 200 |
POST /v1/sessions | 201 |
POST /v1/sessions/refresh | 200 |
POST /v1/api-keys | 201 |
POST /v1/agents | 201 |
POST /v1/agents/{id}/tool-integrations | 201 |
POST /v1/integrations/google-drive/service-accounts | 201 |
GET /v1/ai-gateway | 204 when no gateway is configured |
DELETE /v1/agents/{id}/hooks and DELETE /v1/agents/{id}/session-start-hook | 204 |
Pagination#
| List | Style | Page size |
|---|---|---|
GET /v1/threads | cursor, returns nextCursor | Default 100, max 100 |
GET /v1/credits/usage/periods | cursor, returns nextCursor | Default 12, max 24 |
GET /v1/credits/usage/entries | cursor, returns nextCursor | Default 500, max 1000 |
GET /v1/agents/{id}/traces | page and limit | Default 50, max 100 |
GET /v1/agents/{id}/traces/export | cursor, returns meta.cursor | Varies: pages hold whole traces |
GET /v1/threads/{threadId}/messages | None | The newest 200 messages, with truncated |
Cursors are opaque. Pass back exactly what you received, and stop when it is
null. On threads and credits, a cursor the API didn't issue is refused with
400.
CORS#
The API can be called from a browser on any origin:
Access-Control-Allow-Origin: *, without credentials. Authenticate with theAuthorizationheader, never cookies.- Allowed request headers:
content-type,authorization,connection,cache-controlandx-requested-with. - Readable response headers:
RateLimit-Limit,RateLimit-Remaining,RateLimit-Reset,Retry-Afterandx-runbear-thread-id(the last is still rolling out; see Chat). - Methods:
GET,POST,PUT,DELETE,PATCH,HEADandOPTIONS.
Calling the API from a browser with an API key exposes the key. Use Web SDK session passes for browser traffic.
Related#
- Authentication and API keys
- Streaming — errors inside a stream
- Web SDK errors
- REST API overview