unlob Docs
Browse documentation

Rate limits and quotas

429 and 402 mean opposite things, and one 429 means two different things.

Two independent limits

A per-minute rate limit, set by your plan, applied per key. Exceed it and you get 429. Wait a second and retry; the window is one minute.

A monthly request quota, also set by your plan. What happens when you reach it depends on the plan:

  • On Free, the key hard-caps: further requests get 429 until the period rolls over. There is no card on file, so free means free.
  • On paid plans, requests continue past the quota. The pricing page promises that rather than failing closed — an agent in production should not stop working because a counter rolled over.

The headers

Every authenticated response carries these, on success and on 429 alike:

x-ratelimit-limit: 600
x-ratelimit-remaining: 587

On a rate-limit 429, one more:

retry-after: 1

There is no x-ratelimit-reset. The window is one minute, so retry-after: 1 is the whole story — do not look for a reset timestamp that does not exist.

Telling the two 429s apart

This is the part worth reading carefully. Both a rate-limit refusal and a hard-capped quota return 429, and they call for opposite responses:

Rate limitQuota hard cap
Bodyrate limit exceededmonthly quota exceeded and this key stops at its quota rather than billing overage
retry-after1absent
Retry?Yes, in about a secondNo. It will not change until the period rolls over.

So: retry a 429 only when retry-after is present. A retry loop that ignores the body and hammers a hard-capped key produces thousands of identical failures and never succeeds.

And 402 is different again

402 means the account is suspended — a subscription was cancelled, or a payment failed. Retrying changes nothing; somebody has to settle billing in the console.

Summary:

StatusMeaningRetry?
429 with retry-afterToo fastYes, ~1s
429 without retry-afterOut of monthly quota, hard-cappedNo
402Account suspendedNo
401Bad key — or a key too new to have propagatedOnce, after a minute

Rate limits are per replica

Published rates are enforced per serving replica. A fleet of N replicas therefore tolerates more than the published number in aggregate, and the limit you observe can be higher than the one you are quoted.

Do not build on that. It moves with the fleet size, and the published figure is the one the plan promises. Treat any headroom as luck.

Over MCP

Identical. The same key, the same limits, the same headers on the HTTP response.

One difference in how a refusal reaches you: over MCP, a suspended account or an exhausted quota arrives as a tool error inside the JSON-RPC frame, not as a 402 or 429 status. That is deliberate — a client reads a transport error on an established session as the server having gone away and tears the session down, which turns a billing problem into an apparent outage.

Rate limiting still answers 429 at the transport, because that is what a client’s own backoff logic understands.

Connecting is free: initialize, notifications/initialized, tools/list and ping are not billed. Only a tool that runs counts, and it counts as one request — the same as the equivalent REST call.

Staying inside them

  • Read x-ratelimit-remaining and pace yourself, rather than discovering the limit.
  • Use limit=0 with facets=true to size a topic in one request instead of five exploratory searches.
  • Use collapse=story so you are not spending requests — and context — on the same article repeatedly.
  • assemble_context is one billed call in place of a retrieval loop that would be several.

Next