Rate limits and quotas
How buzzabout regulates traffic — per-key request limits, credits, in-flight caps, and the 429 envelope your client needs to handle.
buzzabout regulates traffic through three mechanisms: a per-API-key request-rate limit, credit-based pre-checks on chargeable operations, and a per-audience-dataset concurrency cap. Build for all three in your client.
Per-API-key request limit
Every authenticated public endpoint — REST (/v1/...) and MCP
(/mcp/...) — is limited to 5 requests per second per API key,
shared across both surfaces. Identity is keyed on the SHA-256 digest
of your API key, so a single key has one rate budget regardless of
how you call the platform.
Exceeding the cap returns 429:
{
"status": "client_error",
"error_code": "too_many_requests",
"detail": "Too many requests. Please slow down.",
"transient": true,
"retry_after_seconds": 0.832
}The transient: true flag is the signal that retrying after a delay
should succeed. retry_after_seconds is the precise wait time the
limiter measured; the standard Retry-After HTTP header carries the
same value rounded up to whole seconds.
Three informational response headers are emitted on every
authenticated request — not just 429s — so a well-behaved client can
adapt its send-rate before being throttled:
| Header | Meaning |
|---|---|
RateLimit-Limit | The cap (5 today). |
RateLimit-Remaining | Calls left in the current second-window. |
RateLimit-Reset | Seconds until the bucket fully resets. |
The recommended client behaviour is exponential backoff with jitter,
preferring the server's Retry-After hint when present:
import random
import time
import httpx
def call_with_backoff(
client: httpx.Client,
method: str,
url: str,
*,
max_attempts: int = 5,
**kwargs,
) -> httpx.Response:
for attempt in range(max_attempts):
resp = client.request(method, url, **kwargs)
if resp.status_code != 429 or attempt == max_attempts - 1:
return resp
delay = float(resp.headers.get("Retry-After", "1"))
delay = min(60.0, delay + random.random() * 0.25)
time.sleep(delay)
return respNeed a higher rate?
If your integration legitimately needs more than 5 req/s sustained, mail support@buzzabout.ai. Enterprise plans are eligible for a higher per-key rate without a code change on your side.
Credit-based pre-checks (402)
Runs (POST /v1/datasets/{id}/runs, POST /v1/audience_datasets/{id}/runs,
POST /v1/pattern_detections, POST /v1/custom_parameters/{id}/runs)
all pre-check the account's credit balance. When it would be exceeded
the response is 402 with a structured detail block telling you
exactly how many credits the call needs and how many you have:
{
"status": "client_error",
"error_code": "insufficient_credits",
"detail": {
"message": "Insufficient credits to start this run.",
"required": "20",
"available": "0",
"top_up_url": "https://app.buzzabout.ai/settings/billing"
},
"transient": false
}Surface required - available in your UI so the user knows exactly
how many credits to top up. Top up via the web app or upgrade your
plan; in-flight requests are never partially charged. See
Pricing for
how the up-front reservation works.
Per-audience-dataset concurrency (409)
A single audience dataset only allows one in-flight run. A second
POST /v1/audience_datasets/{id}/runs while another is pending or
working returns 409:
{
"status": "client_error",
"error_code": "audience_dataset_run_already_in_flight",
"detail": "Another run for this audience dataset is already pending or working. Wait for it to finish before starting a new one.",
"transient": false
}Poll GET /v1/audience_datasets/{id}/runs/{run_id} until the existing
run terminates, then retry. Regular datasets do not have this
restriction — multiple runs can be in flight concurrently against the
same dataset.
Next steps
- Pricing — per-result credit costs and reservation-based charging.
- API overview — response envelope, errors, and pagination model.