NOONOO TRADINGStart in the bot

API Rate Limits and 429 Errors: Why a Bot Can Stop Working Silently

A rate limit is the amount of requests an exchange allows a user within a unit of time. Exceed it and you receive an error such as 429, Too Many Requests, instead of the expected response. The problem is that this can happen silently. The bot keeps running, but prices become stale and stop-loss submissions fail. Many failures that cannot be explained by adjusting the strategy originate here.

Three limits run separately

Names and figures vary by exchange, but the broad structure is similar. Watching only one limit often leaves you blocked by another.

Three types of limit

1. Request count
Requests per time window, such as 20 every 2 seconds.
Often counted separately by endpoint.

2. Weight
Each request has a cost.
A simple price query may cost 1 point; a full order book 50.
They share a total points-per-minute ceiling.

3. Order-specific allowance
Orders and cancellations use a counter separate from queries.
For example, 10 per second or 100 per 10 seconds.

→ Spare query allowance does not prevent the order limit from blocking you.

Actual ceilings differ by exchange, endpoint and account tier, and may change without notice. Design the system to read response headers rather than memorize fixed numbers. The basic structure of exchange APIs is explained in the exchange API guide.

How polling alone consumes the allowance

The most common failure comes from simple repeated queries, not sophisticated features. Multiplying the symbol count by frequency reveals the problem.

A bot querying 10 symbols every second

Requests per second = 10 symbols × 1 = 10/second
Requests per minute = 10 × 60 = 600/minute

Add balance queries every second: 60
Open-order queries every second: 60
Position queries every second: 60

Total = 780/minute

Weights: assume price 1, balance 5, order list 3
Prices: 600 × 1 = 600 points
Balance: 60 × 5 = 300 points
Order list: 60 × 3 = 180 points
Positions: 60 × 3 = 180 points
Total: 1,260 points/minute

→ A 1,200-point minute limit is already exceeded.
→ Increasing to 20 symbols increases the excess further.

The crucial point is that the allowance can be consumed without the strategy making a single trade. Requests continue while it merely watches prices. Increasing the symbol count can therefore introduce live data gaps into logic that appeared fine in a backtest. Other multi-asset considerations appear in the API bot guide.

What follows 429 is more dangerous

A 429 is closer to a warning. The real danger comes next.

What happens after a 429

1. Excess requests → 429 response.
2. The bot treats it as failure and retries immediately.
3. Retries push usage higher again.
4. The exchange temporarily blocks the IP for minutes or tens of minutes.
5. During the block, cancellations and stop-loss submissions also fail.

→ A position remains open, but you cannot act on it.

The fifth stage causes actual trading losses. Failed queries are inconvenient; failed exit orders expose the account to losses. A thoughtless while retry loop can therefore amplify the failure itself. See why orders are rejected for other blocking conditions.

What is counted: IP or account?

Without knowing what the counter is attached to, you may choose an ineffective remedy.

Counter scope

IP-based
Several bots on the same server share the allowance.
→ With 3 bots on one PC, each effectively has one-third.
→ Issuing a new API key does not help.

API-key or account-based
Common for order-related limits.
→ Splitting servers does not help if the account is the same.

Both
Public market data is often IP-based, while private orders are key-based.

This matters because creating another key after repeated 429s has no effect on an IP-based limit. Conversely, adding servers does not remove an account-based order limit. See API keys and permissions for key creation and access settings.

Read remaining allowance from response headers

Most exchanges provide current usage and remaining allowance in response headers. Header names differ, but their role is similar.

A header-based control pattern

• Parse and record usage on every response.
• Above 70%, automatically stop nonessential queries.
• Above 90%, stop all requests except orders.
• If a 429 supplies a retry delay, follow that value.

→ Do not fix the ceiling as a code constant.
→ Adjust using the observed values reported in headers.

Hardcoding a ceiling can stop the whole bot on the day an exchange changes its policy. Header-based control lets it slow down in response. This can substantially reduce maintenance for long-running automated trading.

Reduction method 1: replace polling with subscriptions

This often has the largest effect. REST polling repeatedly asks whether anything changed. A WebSocket subscription receives updates when changes occur.

Live prices for the same 10 symbols

REST polling every second
10 × 60 = 600 requests/minute
36,000 requests in an hour.

WebSocket subscription
1 connection + 10 subscription messages.
Subsequent polling requests: 0.
The server pushes prices.

→ Polling allowance consumption effectively disappears.
→ Delay drops from a polling interval of up to 1 second to near-immediate updates.

Subscriptions require their own management. If the connection drops, stale values may remain silently, leaving an apparently healthy bot making decisions from old prices. Record the last update time and apply freshness checks, such as stopping new entries after 5 seconds without an update. Stale-data errors run in the opposite direction to lookahead bias, but both mean deciding from information that does not match reality.

Reduction method 2: batch individual queries

Many APIs offer an endpoint that returns everything at once, instead of requiring a call for each symbol.

Getting prices for 50 symbols

Individual queries
50 requests × weight 1 = 50 points.
Cost rises linearly with symbols.

All-ticker batch
1 request × weight 40 = 40 points.
Even 500 symbols still cost 40 points.

→ Batching wins once the symbol count exceeds 40.
→ At 500 symbols: 500 versus 40 points, a difference of about 12 times.

Remember that a batch request can have a large weight. Polling all tickers every second for only 5 symbols can cost more. Find the crossover by comparing individual weight × symbol count with batch weight.

Reduction method 3: use different intervals for different data

There is no reason to query everything at the same frequency. Balances do not necessarily change every second.

Separate intervals: improving the 780-request bot

Before
Prices, balances, order lists and positions every 1 second.
= 780 requests/minute.

After
Prices → WebSocket subscription: 0 polling requests.
Balances → every 30 seconds: 2/minute.
Order list → every 10 seconds: 6/minute.
Positions → every 5 seconds: 12/minute.
Plus one immediate refresh after an order-fill event.

Total: approximately 20/minute.

→ 780 → 20: a 97% reduction.

The final line is the key. Longer intervals may seem less responsive, but most important account changes occur when your orders fill. Use longer normal intervals, then query once immediately after the event. This maintains accuracy while reducing requests.

Reduction method 4: exponential backoff and jitter

This determines how long to wait after a 429. Fixed-interval retries can hit the limit again before it has recovered.

Exponential backoff delays

First failure → wait 1 second.
Second failure → 2 seconds.
Third failure → 4 seconds.
Fourth failure → 8 seconds.
Fifth failure → 16 seconds.
Cap the delay at 60 seconds.

Add jitter: random variation
Delay = base × a random value from 0.5 to 1.5.
A 4-second base becomes a delay between 2.0 and 6.0 seconds.

→ Prevents several bots retrying together and causing another surge.

Without jitter, bots on the same server may retry at exactly the same second and exceed the limit again. Spreading retries randomly can noticeably improve recovery. Also cap the retry count. Infinite retries only prolong a block.

Separate order traffic from query traffic

This is a design principle: data queries must not delay orders.

A priority queue design

Priority 1: must not be delayed
Stops, exits and order cancellations.
Always reserve part of the allowance.

Priority 2: delay is acceptable
New entries and position queries.

Priority 3: disposable
Chart-history backfills, statistical collection and queries for logs.
Stop automatically above 70% usage.

→ Decide in code what to give up first
before a shortage or failure happens.

Reserving allowance is the point. Optimizing for 100% utilization can cause the most important orders to fail when volatility spikes and retries surge. Splitting large orders through TWAP execution or iceberg orders increases the order count, so check the dedicated order allowance first.

Failures during data collection and backtesting

Exceeding limits is often more common when collecting historical data than during live operation.

Backfilling 3 years of 1-minute candles

1 year = 525,600 minutes.
3 years = 1,576,800 candles.
Assume 1,000 candles per request.
Required requests: 1,577.

An unrestricted loop
20 requests/second → 1,577 in 79 seconds.
→ Mostly 429s and an IP block.

A 0.2-second interval
1,577 × 0.2 seconds ≈ 5 minutes 15 seconds.
→ Completes without a block in this example.

→ Trying to save 5 minutes can cause a 30-minute block.

A backfill rarely needs to be rushed. Space requests and save the last successful timestamp so an interrupted run can resume. See the backtesting guide for cautions when validating with collected data.

Inspection checklist

Before deploying a bot to a real account

1. Have you calculated and recorded requests per minute and total weight?
2. Have you checked query and order limits separately?
3. Do you read and log usage from response headers?
4. Does 429 handling include exponential backoff, jitter and a retry cap?
5. Do you receive live prices through WebSockets?
6. Do subscriptions have freshness checks?
7. Is allowance reserved for stops and cancellations?
8. Do you know how many bots share one IP?
9. Do repeated 429s alert an administrator?
10. Do backfill and collection scripts space their requests?

The ninth item is often omitted. Exception handling can silently swallow 429s, leaving a bot that appears healthy but does nothing for days. Do not merely log failures; send an external alert after a threshold is crossed.

Summary

1. Request-count, weight and order-specific limits run separately.
2. Polling alone consumes limits even without trades.
3. The IP block after 429 is more dangerous because exits fail.
4. Extra keys do not help an IP-based counter.
5. Read ceilings and usage through headers rather than hardcoding them.
6. Switching polling to subscriptions gives the biggest reduction: 600 requests/minute → 0.
7. Use different data intervals and immediate refreshes after relevant events.
8. Retry with exponential backoff, jitter and a cap.
9. Always reserve allowance for stops and cancellations.
10. Repeated 429s should trigger an alert.

Rate limits are a safety issue. Request volume can seem irrelevant in normal conditions, then block a stop precisely when the market accelerates and retries cluster. Treat the allowance as a budget reserved for urgent moments, rather than something to exhaust.

NOONOO TRADING invites you to follow live trading in our free chat.

Start in the bot

📈 OKX trading fee discount for new registrations

Register for the OKX Fee Discount →