Scoring at scale
This guide is for workloads that need to score dozens to thousands of titles or packages. The shape we recommend:
- Bound concurrency to your plan’s limit (see Rate limits).
- Pipeline don’t batch — there’s no batch endpoint; concurrency is the lever.
- Honour
Retry-After; don’t poll-and-burn. - Budget credits upfront so a runaway job stops cleanly.
Concurrency pattern (Python)
import asyncio
from brightbean import AsyncBrightBean, RateLimitError, InsufficientCreditsError
async def score_one(client, sem, title):
async with sem:
try:
return await client.score(title=title)
except RateLimitError as e:
await asyncio.sleep(e.retry_after or 1)
return await client.score(title=title)
async def main(titles):
sem = asyncio.Semaphore(20) # cap at plan's concurrency limit
async with AsyncBrightBean() as client:
return await asyncio.gather(*(score_one(client, sem, t) for t in titles))
asyncio.run(main(my_titles))
The SDK already retries 429/5xx with backoff — the inner try/except above is only there to demonstrate the shape; you can rely on the default and simplify.
Concurrency pattern (TypeScript)
import { BrightBean } from "brightbean";
import pLimit from "p-limit";
const client = new BrightBean();
const limit = pLimit(20);
const results = await Promise.all(
titles.map((title) => limit(() => client.score({ title })))
);
Budgeting credits
Before a batch:
balance = (await client.usage()).credits_remaining
needed = len(titles) * 1 # title-only = 1 credit each
if balance < needed:
raise RuntimeError(f"need {needed}, have {balance}")
Or, with the MCP envelope, your agent can self-regulate by watching credits_remaining on every response and stopping at a threshold.
Idempotency
Pass Idempotency-Key on POST calls to make safe retries free:
await client.score(
{ title: "..." },
{ idempotencyKey: `score:${hash(title)}` }
);
A repeat within 24 hours returns the original response without consuming credits a second time.
What NOT to do
- Don’t spin up 200 workers and hope for the best. Limiter will reject most calls; you’ll burn
429s and produce nothing. - Don’t keep retrying
4xxs.400,401,402,404are terminal. Only429and5xxshould retry. - Don’t share an
API keyacross unrelated services. Rate-limit buckets are per key. Create a key per app.