zylon-ai/private-gpt · error · QuotaConsumed

Brave Search API quota exhausted (x-ratelimit-remaining=0)

Error message

Brave Search API quota exhausted (x-ratelimit-remaining=0)

What it means

QuotaConsumed raised in BraveSearchProvider._check_response when the response header x-ratelimit-remaining equals '0'. Brave's API attaches per-query rate limit headers; remaining=0 means the allowance (1 query/sec on the free plan, or the monthly quota) is exhausted. It is raised before any status-code handling, so even a 200 response with remaining=0 triggers it.

Source

Thrown at private_gpt/components/web/web_search/providers/brave.py:228

            status = error.get("status")
            parts = []
            if code:
                parts.append(f"code={code}")
            if status:
                parts.append(f"status={status}")
            if detail:
                parts.append(f"detail={detail}")
            if parts:
                return "; ".join(parts)
            return str(error)
        return str(error)

    def _check_response(
        self, response: ClientResponse, response_data: dict[str, Any]
    ) -> None:
        quota_header = response.headers.get("x-ratelimit-remaining")
        if quota_header is not None and quota_header.strip() == "0":
            raise QuotaConsumed(
                "Brave Search API quota exhausted (x-ratelimit-remaining=0)"
            )

        status_code = response.status
        if status_code < 400:
            return

        error = response_data.get("error", "Unknown error")
        error_message = self._extract_error_message(error)
        if status_code == 429:
            logger.debug(f"Brave Search API rate limit exceeded: {error_message}")
            raise RateLimitExceeded(
                f"Brave Search API rate limit exceeded: {error_message}"
            )
        elif status_code == 400:
            raise ValueError(f"Brave Search API invalid token ({error_message})")
        elif status_code >= 500:
            raise Exception(f"Brave Search API server error: {error_message}")

View on GitHub (pinned to 4a030776a3)

Solutions

  1. Back off and retry after the reset window (1s for rate-limit headers; monthly quota resets on billing cycle).
  2. Enable web_search.cached to dedupe repeated queries.
  3. Respect/raise brave.rate_limit spacing and cap num_concurrent_consumers to serialize Brave calls.
  4. Upgrade the Brave plan or rotate to a key with remaining quota.

Example fix

# before
results = await provider.make_query(q, num_links)

# after
try:
    results = await provider.make_query(q, num_links)
except QuotaConsumed:
    await asyncio.sleep(1.1)  # free tier: 1 req/sec
    results = await provider.make_query(q, num_links)
Defensive patterns

Strategy: retry

Type guard

def is_quota_consumed(exc: BaseException) -> bool:
    return type(exc).__name__ == 'QuotaConsumed'

Try / catch

from private_gpt.components.web.web_search.providers.brave import QuotaConsumed

try:
    results = await provider.make_query(q, num_links)
except QuotaConsumed:
    await asyncio.sleep(1.1)  # free tier resets per second
    results = await provider.make_query(q, num_links)

Prevention

When it happens

Trigger: Free-tier key limited to 1 request/second firing two searches back to back; exhausting the monthly free-query quota; concurrency > 1 hitting Brave despite the rate_limit setting (minimum seconds between requests).

Common situations: Bursty traffic (multiple chat sessions searching simultaneously); cached=false so every query hits the API; keys on the free plan used in shared/staging environments burning the monthly allowance.

Related errors


AI-assisted analysis of zylon-ai/private-gpt@4a030776a3 (2026-08-15). Data as JSON: /api/errors/61d395bfa15d1f9e. Report an issue: GitHub.