Throttling
Throttles shield api.sb from request floods with a 429 a caller waits out, and a Limit is a different gate.
Throttling protects api.sb from too many requests too fast. A throttled request answers 429 before anything is recorded: no Action, no Approval, no charge.
Throttles are not Limits
A throttle and a Limit both say "not now", for different reasons. A throttle guards the platform against a flood. A Limit is a business decision about how much a Worker, Mandate or Entitlement may spend or use.
| Throttle | Limit | |
|---|---|---|
| Protects | The platform and its neighbors | The business |
| Counts | Requests per second, and calls in flight | Money, units, or a rate per day or month |
| On breach | 429 and Retry-After; nothing recorded | The Action waits awaitingAction on an Approval |
| Resolved by | Waiting | Someone with authority approves, or the Limit rises |
tidewise.dev sells tide predictions by API. Its Customers' plans allow 10,000 calls a month: a Limit, metered and billed. The burst of 500 calls a script fires in one second meets a throttle, and the script slows down.
Read the headers
Every response carries the caller's current window:
RateLimit-Limit: 100
RateLimit-Remaining: 12
RateLimit-Reset: 1A throttled request returns the rate-limited problem with Retry-After in seconds:
{
"type": "https: //startupbuilder.dev/ code/ api/ errors/ rate-limited",
"title": "Rate limited",
"status": 429,
"detail": "Key key_tidewise_sync sent 100 requests in the last second.",
"fix": "Wait the number of seconds in the `Retry-After` header, then retry."
}Default throttles
| Throttle | Default |
|---|---|
| Requests per live key | 100 per second |
| Requests per test key | 25 per second |
| Requests per namespace, all keys together | 1,000 per second |
do calls in flight per key | 50 |
| Requests per visitor on a publishable key | 10 per second |
Each throttle is a Setting on the namespace, and a Startup tightens any of them for its own keys. It throttles its own Customers the same way. A plan's per-second rate answers 429, while its monthly count stays a Limit.
Queued work is never throttled
Throttles count requests, not work. Once a send call is accepted, its Action waits queued and runs at the namespace's pace, never answering 429 later.
To move 50,000 records, send them in batches of 100. The queue absorbs the burst, and each Action settles in order.
Back off and retry
The SDK and the CLI retry a 429 on their own. They wait Retry-After plus random jitter, up to three times, with the same idempotency key. Over MCP, the tool result carries the same problem, and an Agent waits before its next call.
A hand-written client does the same:
async function call(req: () => Promise<Response>, attempts = 3): Promise<Response> {
const res = await req()
if (res.status !== 429 || attempts === 0) return res
const wait = Number(res.headers.get('Retry-After') ?? 1) * 1000
await new Promise((r) => setTimeout(r, wait + Math.random() * 250))
return call(req, attempts - 1)
}Never retry a Limit. An Action over a Limit is already waiting for its Approval, and a retry returns that same Action.
Next
- Idempotency: make every retry safe.
- Rate limits: the capability that meters calls against Limits.
- Mandates and Limits: how Limits bound an Agent's authority.