Coding

API Rate Limits, Retries and Backoff for Bots

How to handle 429 errors, timeouts and transient API failures without creating duplicate orders or retry storms.

Illustration showing a trading bot encountering an API rate limit and connection error
Illustration: retry logic needs backoff and state checks, not blind repeated requests.

How to handle 429 errors, timeouts and transient API failures without creating duplicate orders or retry storms.

Updated 2026-09-06CodingPractical guide2 min read

In plain English

This guide focuses on making data and code predictable: define types, validate inputs, handle errors explicitly and test the cases that should fail.

schematypesvalidationerror handling
Practical example

Retry example

If an API says “try again later,” wait with backoff. If the previous request may have created an order, check current broker state before sending the same order again.

Retry reads more freely than writes

Repeating a price request is usually less dangerous than repeating an order request. For write operations, a timeout is ambiguous: the remote side may have accepted the order even though your client never received the response.

Exponential backoff with jitter

import random, time

def delay(attempt, base=0.5, cap=20):
    return min(cap, base * (2 ** attempt)) + random.uniform(0, 0.25)

for attempt in range(5):
    r = call_api()
    if r.status_code != 429:
        break
    time.sleep(delay(attempt))

Idempotency before retries

If the provider supports an idempotency key, use it. If it does not, maintain your own event/order intent table and reconcile broker state before resubmitting an uncertain action.

Respect Retry-After

When an API supplies Retry-After, prefer it over your own guessed delay. Log rate-limit responses separately from network failures so you can see whether the bot design is too chatty.

Bad pattern: retry a failed order ten times in a tight loop. This can turn a temporary network problem into multiple positions or a broker ban/rate limit.

Before you rely on this in production

  • Input types are validated.
  • Errors are handled instead of swallowed.
  • Tests include invalid and boundary cases.
  • Secrets are not embedded in code or test fixtures.