Rate Limiting Strategies: How to Maximize Your API Quota
Practical techniques for working within API rate limits. Learn caching, request batching, smart polling, and quota management to get the most out of every API call.
Why Rate Limits Exist
Rate limits protect both you and the API. Without them, a single runaway loop in your code could burn through your entire monthly quota in minutes. The TickAtlas API enforces per-minute and per-day limits based on your plan tier. Here is how to work within those limits efficiently.
Strategy 1: Client-Side Caching
The single most effective optimization. Cache API responses locally and only fetch new data when the cache expires. Real-time data has a 2-second freshness window; indicator data on H1 only changes every hour.
import requests
import time
from functools import lru_cache
API_KEY = "your_api_key_here"
BASE_URL = "https://tickatlas.com/v1"
HEADERS = {"X-API-Key": API_KEY}
class CachedClient:
def __init__(self):
self._cache = {}
self._cache_ttl = {}
def get_indicators(self, symbol: str, timeframe: str,
indicators: str, ttl: int = 60) -> dict:
"""Fetch indicators with local caching."""
cache_key = f"{symbol}:{timeframe}:{indicators}"
now = time.time()
# Return cached if still fresh
if cache_key in self._cache:
if now - self._cache_ttl[cache_key] < ttl:
return self._cache[cache_key]
# Fetch fresh data
resp = requests.get(f"{BASE_URL}/indicators", params={
"symbol": symbol,
"timeframe": timeframe,
"indicators": indicators
}, headers=HEADERS)
resp.raise_for_status()
data = resp.json()
self._cache[cache_key] = data
self._cache_ttl[cache_key] = now
return data
# Usage: H1 indicators cached for 55 minutes
client = CachedClient()
data = client.get_indicators("EURUSD", "H1", "RSI_14,MACD", ttl=3300) Strategy 2: Request Batching
Fetch multiple indicators in a single API call instead of making separate requests for each one. One call with 5 indicators costs the same as one call with 1 indicator.
# BAD: 5 separate API calls (5 requests)
rsi = client.get("/indicators?symbol=EURUSD&indicators=RSI_14")
macd = client.get("/indicators?symbol=EURUSD&indicators=MACD")
bb = client.get("/indicators?symbol=EURUSD&indicators=BBANDS_20")
adx = client.get("/indicators?symbol=EURUSD&indicators=ADX_14")
atr = client.get("/indicators?symbol=EURUSD&indicators=ATR_14")
# GOOD: 1 API call with all indicators (1 request)
all_data = client.get(
"/indicators?symbol=EURUSD&indicators=RSI_14,MACD,BBANDS_20,ADX_14,ATR_14"
)
# 80% reduction in API usage Strategy 3: Smart Polling Intervals
Match your polling frequency to the timeframe you are analyzing. There is no reason to check H1 indicators every 30 seconds — the data only updates once per hour.
| Timeframe | Recommended Interval | Requests/Hour |
|---|---|---|
| M5 | 60 seconds | 60 |
| M15 | 3 minutes | 20 |
| H1 | 5 minutes | 12 |
| H4 | 15 minutes | 4 |
| D1 | 1 hour | 1 |
Strategy 4: Exponential Backoff
When you hit a rate limit (HTTP 429), do not immediately retry. Wait, then try again with increasing delays.
import time
def request_with_backoff(url: str, params: dict,
max_retries: int = 5) -> dict:
"""Make API request with exponential backoff on rate limits."""
for attempt in range(max_retries):
resp = requests.get(url, params=params, headers=HEADERS)
if resp.status_code == 200:
return resp.json()
if resp.status_code == 429:
wait = 2 ** attempt # 1, 2, 4, 8, 16 seconds
retry_after = resp.headers.get("Retry-After", wait)
print(f"Rate limited. Waiting {retry_after}s (attempt {attempt + 1})")
time.sleep(float(retry_after))
else:
resp.raise_for_status()
raise Exception(f"Max retries ({max_retries}) exceeded") Strategy 5: Usage Monitoring
class UsageTracker:
"""Track API usage to stay within limits."""
def __init__(self, daily_limit: int):
self.daily_limit = daily_limit
self.calls_today = 0
self.reset_date = None
def can_make_request(self) -> bool:
today = datetime.utcnow().date()
if self.reset_date != today:
self.calls_today = 0
self.reset_date = today
return self.calls_today < self.daily_limit
def record_request(self):
self.calls_today += 1
def remaining(self) -> int:
return max(0, self.daily_limit - self.calls_today)
def usage_percent(self) -> float:
return (self.calls_today / self.daily_limit) * 100
# Usage
tracker = UsageTracker(daily_limit=50000) # Starter plan
if tracker.can_make_request():
data = client.get_indicators("EURUSD", "H1", "RSI_14,MACD")
tracker.record_request()
print(f"Usage: {tracker.usage_percent():.1f}% ({tracker.remaining()} remaining)")
else:
print("Daily limit reached. Serving from cache only.") Strategy 6: Priority-Based Fetching
When running low on quota, prioritize the most important data. Fetch only the symbols with active signals rather than scanning everything.
def priority_scan(tracker: UsageTracker,
watchlist: list[str]) -> list[dict]:
"""Scan with priority when quota is low."""
usage_pct = tracker.usage_percent()
if usage_pct < 50:
# Plenty of quota — scan everything
symbols_to_check = watchlist
elif usage_pct < 80:
# Getting tight — only check top 5 pairs
symbols_to_check = watchlist[:5]
else:
# Almost out — only check pairs with recent signals
symbols_to_check = get_pairs_with_recent_signals()
results = []
for symbol in symbols_to_check:
if tracker.can_make_request():
data = client.get_indicators(symbol, "H1", "RSI_14")
tracker.record_request()
results.append(data)
return results Quick Wins Summary
Batch indicators in one call
Saves 60-80% of requests immediately. The single biggest optimization.
Cache H1+ data for 5 minutes
H1 candles close once per hour. Fetching more often than every 5 minutes is wasteful.
Handle 429 errors gracefully
Exponential backoff prevents cascade failures. Read the Retry-After header.
Further Reading
Try this with live data
Every account gets $2.50 in free PAYG credits. No card required — paste your API key and run the code above against live broker data.
Keep reading
All articles- Developer 10 min read
Caching Financial Data: Redis Patterns for Trading Applications
Learn smart caching strategies for financial data using Redis. Reduce API costs, improve latency, and maintain data freshness with TTL-based patterns.
March 28, 2026
- Developer 10 min read
Error Handling in Trading Systems: Why It Matters More Than You Think
Trading systems fail differently than web apps. Learn the error handling patterns that prevent small bugs from becoming expensive losses.
March 28, 2026
- Developer 11 min read
REST API Best Practices for Financial Data Applications
Essential patterns for building reliable financial applications on top of REST APIs: retry logic, rate limiting, data validation, and error handling.
March 28, 2026