Trading bot API rate limits: 429 errors, backoff and queues
Exchange APIs limit how much traffic a bot may send. A bot that ignores those limits often fails at the worst moment: market-data polling consumes the budget, then a cancel or risk check is throttled. The fix is a central request scheduler that understands weights, reserves emergency capacity, uses streams for live data, and treats order retries differently from harmless reads.
How exchange rate limits are measured
A rate limit is a rule that caps API activity during a time window. The details are venue-specific and change over time, so never paste a number from a comparison article into production. Read the current documentation for every endpoint your bot uses. For examples of different models, compare the official Coinbase Exchange REST rate-limit documentation with Kraken's trading rate-limit documentation.
| Limit model | What gets counted | Common surprise |
|---|---|---|
| Requests per window | Calls per second or minute | Bursts fail even when the minute average looks low |
| Weighted requests | Each endpoint consumes a different number of units | One broad snapshot can cost many simple calls |
| Token bucket | Tokens refill over time; each call spends tokens | Short bursts are allowed, then capacity disappears |
| Order activity | New, amended, or cancelled orders | Cancel-replace loops can exhaust it rapidly |
| Connection/message | WebSocket connects, subscriptions, or messages | Reconnect storms create their own ban risk |
Scopes may be per IP address, API key, account, subaccount, trading pair, or endpoint group. Two bot processes using the same account may therefore consume one shared allowance even when each appears below its own local limit. Response headers or API payloads sometimes expose used weight and reset time; record them when available.
Build one request budget for the whole bot
Rate limiting belongs in a shared exchange gateway, not scattered sleep() calls inside strategies. A central scheduler knows the real combined load and can give safety actions priority.
| Priority | Examples | Policy near the limit |
|---|---|---|
| Critical | Cancel, reduce-only exit, kill-switch snapshot | Reserved capacity; never blocked by research work |
| High | Order status, positions, balance reconciliation | Run promptly; coalesce duplicate requests |
| Normal | Signal-time reads not available on a stream | Queue within a freshness deadline |
| Low | History downloads, dashboards, analytics | Defer or drop when capacity is tight |
Subscribe once to a WebSocket feed and fan the data out internally instead of letting every strategy poll the same ticker. Cache immutable market metadata and briefly cache safe account reads when exact real-time values are not required. Batch supported queries. Add deadlines: a quote that arrives after the strategy's decision window should be discarded rather than executed late.
Libraries can help, but configuration is not a guarantee. The ccxt guide shows its built-in throttling option; your application still has to coordinate multiple processes and prioritize risk traffic.
Handle 429 with bounded exponential backoff and jitter
HTTP 429 Too Many Requests or an equivalent venue error means slow down. Honor a valid Retry-After or reset value when the venue supplies one. Otherwise, use exponential backoff capped at a sensible maximum, plus random jitter so multiple workers do not wake and collide at the same instant.
python · retry reads, not unknown writesimport random, time
def retry_read(request, attempts=5):
for n in range(attempts):
response = request()
if response.status != 429:
return response
delay = min(8.0, 0.25 * (2 ** n))
time.sleep(delay + random.uniform(0, delay * 0.25))
raise RateLimitUnavailable()
Retries must be bounded. A long retry queue can become stale work that floods the venue after recovery. Apply a circuit breaker: after repeated throttles, pause noncritical traffic, mark data unhealthy, and let the kill switch block new entries if execution safety is uncertain.
The bot should reduce demand, discard obsolete work, share one cooldown across workers, and expose a degraded state. Without those controls, each sleeping task eventually returns and recreates the same traffic spike.
Order actions need idempotency, not blind retry
Read requests such as fetching a ticker are normally safe to repeat. Write requests are different. If an order submission times out, the exchange may have accepted it even though the bot saw no response. Sending the same trade again can double exposure.
- Assign a unique, persistent client order ID before submitting.
- If the result is ambiguous, mark the intent
UNKNOWNand block conflicting actions. - Query by client ID and inspect recent fills and open orders.
- Submit a replacement only after proving the original was not accepted.
- Reconcile resulting position and protection before strategy execution continues.
Cancels can be ambiguous too: an order may fill while the cancel is in flight. Treat “order not found” as a reason to fetch final state, not proof that nothing happened. The complete execution pattern is covered in partial fills and order reconciliation.
Monitor the budget and test exhaustion
Log endpoint, weight, scope, latency, response code, retry count, queue depth, and reported remaining allowance without logging secrets. Alert before the hard limit, not only after 429s begin. Useful dashboards show consumption by strategy so one noisy component is easy to isolate.
Test with a fake exchange adapter that returns 429s, slow responses, incomplete pages, and changing reset headers. Verify that:
- critical cancels retain reserved capacity;
- duplicate ticker requests collapse into one call;
- stale queued signals expire instead of trading late;
- workers share the same cooldown and add jitter;
- ambiguous writes are queried by client ID, never blindly replayed;
- a reconnect storm is rate-limited and does not spin indefinitely;
- the bot pauses safely when account state cannot be refreshed.
Finally, keep rate-limit rules in configuration with a review date and link to the venue source. Limits can change, account tiers can differ, and a sandbox may not behave like production.
Frequently asked questions
What happens when a trading bot exceeds an API rate limit?
The venue commonly rejects or delays requests, often with HTTP 429 or a venue-specific error. Continued traffic can extend the throttle or trigger a temporary ban. The bot should stop nonessential calls, honor Retry-After or reset information when supplied, and retry with bounded backoff and jitter.
Should a trading bot retry every 429 error?
Only safe and necessary operations should be retried. Read requests are usually repeatable, but an order submission may have succeeded before its response was lost. Query by client order ID before retrying any ambiguous write so the bot cannot duplicate a trade.
Do WebSockets eliminate exchange API rate limits?
No. WebSockets reduce repetitive market-data polling, but connection attempts, subscriptions, messages, and private trading operations can have separate limits. Bots still need a request budget and a REST path for snapshots and reconciliation.
How can multiple strategies share one exchange API limit?
Route all calls through a central scheduler that understands endpoint weights and venue scopes. Reserve capacity for cancels, risk checks, and reconciliation; coalesce duplicate reads; cache safe responses briefly; and make research or historical downloads use a separate low-priority budget.