Skip to main content
BotShield limits requests per credential. Each key type has several windows (called buckets), and a request is blocked when any one of them is full. A blocked request gets HTTP 403 with code: "RATE_LIMITED". It does not get HTTP 429, and there is no Retry-After header, so your client has to read the response body.

Limits by key type

Every operation call made with a credential counts once against that credential. Limits are per key, not per organization, so two API keys have separate counters. Development limits are sized for building and testing. They are deliberately too low to run production traffic on a development key. The per_ip bucket on live site keys counts each visitor’s IP address separately. One visitor who hammers your page reaches it long before your key-wide limits are affected. Agent keys (bs_agent_…) have no published limit. Build your agent as if one applies: use the long-poll on GET /agentlink/check-status (wait_seconds up to 25) instead of a tight polling loop, and reuse a request_id when you retry a proposal. If your production traffic needs higher limits, contact BotShield. Limits can be raised for a specific key.

The blocked response

The blocked request did not run. Nothing was created or consumed, so it is safe to send again after the wait.

Watch your usage

Successful responses carry the same _rateLimit object next to data, so you can slow down before you are blocked:
The example is shortened. The data part is the operation’s normal response.
  • For API keys, test site keys, and grant tokens, _rateLimit is on every response.
  • For live site keys (pk_live_…), successful responses leave it out to keep browser traffic small. Send the header x-botshield-include-ratelimit: true to include it. A blocked response always includes it.

Back off and retry

Wait for _rateLimit.primary.resetIn seconds, add a little random jitter so parallel workers do not all retry together, and cap the number of attempts.
If the full bucket is hourly or daily, resetIn is long. Do not hold a user’s request open for it. Return an error to your caller, alert your team, and look for the cause.
The TypeScript SDK’s built-in retry does not cover rate limits. Its default retry codes are 429 and 5xx, and a rate-limited call throws a BotShieldDefaultError with statusCode 403. Catch it and back off yourself. See TypeScript SDK.

Stay under the limits

  • Create sessions on demand. Call POST /sdk/create-session when a user is about to verify, not on every page load. Each session also leads to a create-verification-link call.
  • Poll at a steady pace. When you poll GET /verification/status from your server, every 2 to 3 seconds is enough. A request lives five minutes.
  • Use the long-poll for Agents Ask. One check-status call with wait_seconds=25 replaces a dozen short polls.
  • Use webhooks for outcomes you do not need instantly. See Webhooks overview.
  • Keep development and production apart. Load tests against a development key hit the development limits quickly.

Next steps

Errors

All error shapes and a typed unwrap helper.

API overview

Credentials and the response envelope.

Keys and environments

Which key belongs where.

Testing

Try a gate in Development.