brime

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

codeStatusRetry?Meaning
unauthorized401NoMissing, malformed, or revoked API key.
insufficient_credits402After top-upAccount balance is zero for the current period.
invalid_request400After fixMalformed body, missing required header, schema violation.
not_found404NoResource (e.g., job_id) doesn't exist.
rate_limited429With backoffPer-key budget hit. Honor Retry-After.
upstream_error502/503/504With backoffSERP, LLM, extract worker, or browser tier degraded.
internal_error500OnceUnexpected engine error. Includes a request_id for support.

Retry policy

A safe, generic policy:

CodeRetryBackoff
unauthorizedNo—
insufficient_creditsNo (top-up needed)—
invalid_requestNo (fix code)—
not_foundNo—
rate_limitedYesHonor Retry-After, then exponential, max 5 attempts
upstream_errorYesExponential 2 s → 30 s, max 3 attempts
internal_errorYes (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:

AdapterBrime codeAdapter shape
TavilyAll{detail: {error: <message>}} (Tavily SDK shape)
ExaAll{error: {message: <message>}}
ParallelAll{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.