# Throttling

URL: /code/api/guides/throttling · Id: `page:code/api/guides/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](/code/platform/api-sb) from too many requests too fast. A throttled request answers `429` before anything is recorded: no [Action](/business/nouns/action), no [Approval](/business/nouns/approval), no charge.

## Throttles are not Limits
A throttle and a [Limit](/business/nouns/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](/business/nouns/worker), [Mandate](/business/nouns/mandate) or [Entitlement](/business/nouns/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](/business/nouns/customer)' 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](/code/api/errors/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
| 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](/business/nouns/setting) on the namespace, and a [Startup](/business/nouns/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](/business/nouns/agent) waits before its next call.

A hand-written client does the same:

```ts
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](/code/api/guides/idempotency): make every retry safe.
- [Rate limits](/code/platform/rate-limits): the capability that meters calls against Limits.
- [Mandates and Limits](/agents/mandates-and-limits): how Limits bound an Agent's authority.