Skip to content

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.

ThrottleLimit
ProtectsThe platform and its neighborsThe business
CountsRequests per second, and calls in flightMoney, units, or a rate per day or month
On breach429 and Retry-After; nothing recordedThe Action waits awaitingAction on an Approval
Resolved byWaitingSomeone 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:

HTTP
RateLimit-Limit: 100
RateLimit-Remaining: 12
RateLimit-Reset: 1

A throttled request returns the rate-limited problem with Retry-After in seconds:

JSON
{
  "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

ThrottleDefault
Requests per live key100 per second
Requests per test key25 per second
Requests per namespace, all keys together1,000 per second
do calls in flight per key50
Requests per visitor on a publishable key10 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:

TypeScript
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