Errors & limits

Error format

Request-level failures (before the stream starts) return an HTTP error status and a JSON body with a consistent shape:

json
{
  "error": {
    "type": "authentication_error",
    "code": "INVALID_API_KEY",
    "message": "Invalid or missing API key."
  }
}

Status codes

StatusCodeMeaning
400MISSING_QUERYThe query field is missing or empty.
400QUERY_EXCEEDS_MAX_LENGTHquery is longer than 10,000 characters.
400INVALID_REASONING_LEVELreasoning_level is not low, medium, or high.
400INVALID_REQUESTThe body is not valid JSON.
401INVALID_API_KEYMissing, malformed, or revoked credential.
402INSUFFICIENT_CREDITSNot enough credits for this request.
429RATE_LIMIT_EXCEEDEDToo many concurrent requests for your tier.
404NOT_FOUNDUnknown endpoint.
502PROXY_ERRORThe research backend was unreachable.

Errors that occur after the stream has started are delivered as a research.failed event instead — see Streaming & events.

Credits

Each request costs credits based on reasoning_level, which sets the maximum limit of research iterations. The underlying model decides dynamically when it has enough context to answer, meaning it may terminate early and perform fewer iterations than the specified ceiling.

Reasoning levelMax IterationsCredits
low510
medium1015
high1520

New accounts start with 300 free credits per month, renewed on your registration anniversary date. Credits are deducted before research begins; if your balance is too low you get 402 INSUFFICIENT_CREDITS with a Retry-After header.

Concurrency limits

Bahith limits the number of simultaneous in-flight requests per account by tier:

TierConcurrent requests
Free1
Pro3
Scale5

Exceeding your limit returns 429 RATE_LIMIT_EXCEEDED with a Retry-After header (seconds). This caps concurrency, not total throughput — retry after the indicated delay.

Handling 429 and 402

Respect the Retry-After header. For 429, wait the indicated seconds and retry; for sustained load, queue requests up to your tier's concurrency limit. For 402, top up credits before retrying.

bash
research_with_retry() {
  local query="$1"
  local key="$2"
  local max_retries=3
  for ((attempt=1; attempt<=max_retries; attempt++)); do
    response=$(curl -s -i -N -X POST https://api.bahith.dev/v1/research \
      -H "Content-Type: application/json" \
      -H "X-API-Key: $key" \
      -d "{\"query\": \"$query\"}")
    
    status_code=$(echo "$response" | grep "HTTP/" | awk '{print $2}')
    
    if [ "$status_code" -eq 429 ]; then
      retry_after=$(echo "$response" | grep -i "Retry-After" | awk '{print $2}' | tr -d '\r')
      retry_after=${retry_after:-5}
      echo "Rate limited. Retrying in $retry_after seconds..."
      sleep "$retry_after"
      continue
    fi
    echo "$response"
    return 0
  done
  echo "Failed after $max_retries attempts" >&2
  return 1
}