Rate Limits & Resilience: Design Trading Bots That Don’t Break
December 25, 2025

Most bots don’t fail because of strategy — they fail because of plumbing
In real markets, the difference between a stable system and a broken one is rarely “smarter signals.” It’s things like retries, timeouts, caching, and rate-limit awareness. Mila-ex makes the data consistent; your job is to make your client resilient.
Know the endpoints (and the expensive ones)
These are the most common calls in a market-data pipeline:
- /api/v1/exchange (discover exchanges \+ capabilities)
- /api/v1/exchange/markets (market catalog per exchange)
- /api/v1/exchange/ticker and /api/v1/exchange/tickers (price snapshots)
- /api/v1/exchange/ohlcv (candles; typically heavily rate-limited)
- /api/v1/exchange/orderbook and /api/v1/exchange/orderbook/complete (depth \+ prints)
Golden rule: cache what doesn’t change quickly
- Exchanges list: cache for hours
- Markets list: cache for minutes (or longer) and refresh in the background
- Tickers: cache for seconds (depending on your use case)
- OHLCV: cache aggressively; candles are not tick-by-tick
Error handling: treat errors as data
Mila-ex may return a structured error payload (with Code, Message, and detailed Errors). When your bot sees errors, it should decide whether to retry, switch exchanges, or degrade gracefully.
Retry strategy (simple and effective)
- Retry only on transient errors: timeouts, 5xx, network failures
- Backoff: exponential \+ jitter
- Stop conditions: max attempts; circuit breaker when an exchange is unhealthy
TypeScript: resilient fetch wrapper
async function sleep(ms: number) {
return new Promise(resolve =\> setTimeout(resolve, ms));
}async function fetchJsonWithRetry(url: string, apiKey: string, attempts = 4) {
let lastErr: unknown; for (let i = 0; i \< attempts; i++) {
try {
const res = await fetch(url, {
headers: { "x-api-key": apiKey },
}); if (\!res.ok) {
const text = await res.text();
// Non-2xx could be supported/unsupported operations or other errors.
// Decide your policy: retry on 5xx, otherwise fail fast.
if (res.status \>= 500) throw new Error(text);
throw new Error(text);
} return await res.json();
} catch (e) {
lastErr = e;
const backoff = Math.min(2000, 250 * Math.pow(2, i));
const jitter = Math.floor(Math.random() * 120);
await sleep(backoff \+ jitter);
}
} throw lastErr;
}Design pattern: capability-aware routing
Use GET /api/v1/exchange to learn whether an exchange supports the operation you want (tickers, order books, candles). Then route traffic accordingly. This avoids wasted calls and improves uptime.
Related reading
- 5 Common Mistakes When Building Crypto Trading Bots
- Real-Time Tickers Across Exchanges
- Building an Arbitrage Bot Using Mila-ex in 10 Minutes
- From CCXT to Mila-ex (Migration Playbook)
Reference
Endpoint definitions and models are documented in the Mila-ex API Docs.