Agent workloads hit rate limits in bursts: a batch of signups that all wait for codes at once, a loop that sends a follow-up to every lead, twenty workers that start together after a deploy. The fix is easier when you know exactly which limit you hit, what counts toward it, and what the API sends back. These are the limits Lumbox enforces in its API code, with the numbers, followed by the patterns that keep an agent fleet under them.
The limits
| Limit | Value | When exceeded |
|---|---|---|
| Requests per API key | 120 per minute, fixed 60-second window | 429, retry_after_seconds in the body |
| Sends per organisation per minute | Free 3, Starter 30, Pro 60, Scale 120 | 429, "Send rate limit exceeded" |
| New-account ramp (Free, no verified domain) | 5 sends in the first hour, 25 in the first 24 hours | 429, code: "SEND_RAMP_LIMIT" |
| Sends per month | Free 100, Starter 2,000, Pro 10,000, Scale 50,000 | 402, code: "PLAN_LIMIT_EXCEEDED" |
| Inboxes per organisation | Free 3, Starter 10, Pro 50, Scale 250 | 402, code: "PLAN_LIMIT_EXCEEDED" |
| Messages per bulk request | 100 | 400 |
Some per-request ceilings also shape how you call the API. A long-poll on /wait or /otp holds for 1 to 120 seconds (30 by default), the SSE stream for up to 900. Email lists return at most 100 per page and inbox lists 200. Inbound messages can be up to 25 MB. The monthly received figure on each plan is an allowance reported by GET /v1/orgs/me, not a per-request limit, so nothing below applies to it.
What counts, and against whom
The 120-per-minute limit counts every authenticated request made with a key, and it counts each request once. A /wait call that the server holds open for 60 seconds is one request; the once-a-second checks it makes internally are not requests. Each API key has its own counter, so two workers with two keys each get 120.
The send limits work differently. The per-minute send count and the monthly quota belong to the organisation, and every key in it draws from the same pool. Giving each agent its own key spreads request load but does not raise how many emails the org can send per minute. The new-account ramp applies to Free orgs in their first 24 hours; paid plans and orgs with a verified custom domain skip it.
Webhook deliveries are requests Lumbox makes to you, so they cost nothing against your key. Anything your webhook handler calls back into the API does count.
Reading a 429
Every response on a rate-limited route carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds). Send endpoints add X-Send-RateLimit-Limit and X-Send-RateLimit-Remaining. There is no Retry-After header; the wait time is in the JSON body:
{"error": "Rate limit exceeded. Try again later.", "retry_after_seconds": 23}
This matters for SDK behaviour. The TypeScript SDK retries a 429 twice with exponential backoff that starts at 500 milliseconds and caps at 8 seconds, and it looks for a Retry-After header that is not there. Inside a fixed 60-second window, both retries can land before the window resets. The Python SDK does not retry at all. For bursty workloads, handle 429 yourself and sleep for what the body says:
import time
import uuid
import httpx
API = "https://api.lumbox.co/v1"
client = httpx.Client(headers={"X-API-Key": "ak_..."}, timeout=httpx.Timeout(10.0, read=130.0))
def request(method, path, attempts=4, **kwargs):
for i in range(attempts):
r = client.request(method, f"{API}{path}", **kwargs)
if r.status_code != 429:
return r
body = r.json()
if body.get("code") == "SEND_RAMP_LIMIT":
return r
time.sleep(body.get("retry_after_seconds", 2 ** i))
return r
def send(inbox_id, to, subject, text):
return request(
"POST", f"/inboxes/{inbox_id}/send",
json={"to": to, "subject": subject, "text": text},
headers={"Idempotency-Key": str(uuid.uuid4())},
)
A ramp 429 is returned immediately because retrying within seconds cannot succeed; the ceiling is measured over the first hour and first day of the account. A 402 is not retried either, since only an upgrade or the next month changes it.
Retrying sends without sending twice
The send helper generates one Idempotency-Key per logical email and reuses it across attempts. Lumbox honours the header on POST, PUT, PATCH and DELETE under /v1/inboxes/ and /v1/send/ for 24 hours. When a send succeeded but the response was lost to a timeout, the retry gets the stored response back with Idempotent-Replayed: true and no second email goes out. Only successful responses are stored, so a retry after a 429 or a 5xx runs normally with the same key. Reusing a key with a different body returns 409.
Patterns that stay under the limits
Wait with long-poll, not a loop. Twenty agents polling every 2 seconds make 600 requests a minute, five times the per-key limit. The same twenty agents each holding a 60-second /wait make at most 20 a minute while idle. The long-poll guide covers the details.
Pace sends to the plan. On Free, 3 sends a minute means a loop of 10 follow-ups takes over three minutes. Queue sends and drain the queue at the org's rate instead of letting each agent send when it is ready and collect 429s. POST /v1/send/bulk takes up to 100 messages in one request and counts as one request toward the per-minute send limit, while each message still counts toward the monthly quota. It needs an org-level key, goes out through Resend, and rejects the whole batch with a 402 if the batch would pass the monthly quota.
Delete inboxes you are done with. The inbox limit counts every inbox that exists in the org, and inboxes do not expire on their own. An agent that creates an inbox per task should call DELETE /v1/inboxes/:id at the end of it. Pre-creating a pool of inboxes and reusing them saves only one request per task, and a reused inbox still holds the previous task's mail, so any wait on it needs a since filter.
Use webhooks for many inboxes. Past a few dozen inboxes, one webhook for the org plus a periodic sweep costs a few requests a minute, where one long-poll per inbox costs one request per inbox per timeout. Webhooks vs polling works through that trade.
GET /v1/orgs/me returns this month's sends and the org's inbox count next to their limits under usage.remaining, which is worth checking before a large batch. Plan quotas and prices are on the pricing page.