Errors
Brime's error wire format and the full code catalog.
Every Brime error follows the same shape:
{
"error": {
"code": "rate_limited",
"message": "Rate limit exceeded. Wait 12 s and retry.",
"details": null
}
}The HTTP status carries the class; error.code is the stable machine identifier you should branch on. error.message is human-friendly and may evolve — don't pattern-match it.
Code catalog
code | Status | Retry? | Meaning |
|---|---|---|---|
unauthorized | 401 | No | Missing, malformed, or revoked API key. |
insufficient_credits | 402 | After top-up | Account balance is zero for the current period. |
invalid_request | 400 | After fix | Malformed body, missing required header, schema violation. |
not_found | 404 | No | Resource (e.g., job_id) doesn't exist. |
rate_limited | 429 | With backoff | Per-key budget hit. Honor Retry-After. |
upstream_error | 502/503/504 | With backoff | SERP, LLM, extract worker, or browser tier degraded. |
internal_error | 500 | Once | Unexpected engine error. Includes a request_id for support. |
Retry policy
A safe, generic policy:
| Code | Retry | Backoff |
|---|---|---|
unauthorized | No | — |
insufficient_credits | No (top-up needed) | — |
invalid_request | No (fix code) | — |
not_found | No | — |
rate_limited | Yes | Honor Retry-After, then exponential, max 5 attempts |
upstream_error | Yes | Exponential 2 s → 30 s, max 3 attempts |
internal_error | Yes (once) | 1 s |
Idempotent endpoints (extract, deep research) are safe to retry — pass the same Idempotency-Key to dedupe.
import time
from brime import Brime, RateLimitError, UpstreamError
client = Brime()
def call_with_retry():
for attempt in range(5):
try:
return client.search(query="...")
except RateLimitError:
time.sleep(min(2 ** attempt, 30))
except UpstreamError:
time.sleep(2)
raise RuntimeError("retries exhausted")import { Brime, RateLimitError, UpstreamError } from "@brime-ai/brime";
const client = new Brime();
async function callWithRetry() {
for (let attempt = 0; attempt < 5; attempt++) {
try {
return await client.search({ query: "..." });
} catch (e) {
if (e instanceof RateLimitError) {
await new Promise(r => setTimeout(r, Math.min(2 ** attempt, 30) * 1000));
} else if (e instanceof UpstreamError) {
await new Promise(r => setTimeout(r, 2_000));
} else {
throw e;
}
}
}
throw new Error("retries exhausted");
}Adapter error mapping
When you hit /tavily/*, /exa/*, or /parallel/*, Brime translates errors into each vendor's wire format so SDKs work unchanged:
| Adapter | Brime code | Adapter shape |
|---|---|---|
| Tavily | All | {detail: {error: <message>}} (Tavily SDK shape) |
| Exa | All | {error: {message: <message>}} |
| Parallel | All | {detail: <message>} |
The HTTP status code is preserved — only the body shape changes.
Including request_id in support tickets
Every response (success or error) includes a request_id (header X-Request-Id and body request_id). When opening a ticket, paste it — we can pull the full request/response trace from logs.