Rate Limits
| Plan | Monthly CU Pool | Trace CU Pool | Throughput (CUPs) | Chains |
|---|---|---|---|---|
| Starter | 400M CUs | No access | 50 CUs/sec | 17 (all) |
| Growth | 2B CUs | 100M | 330 CUs/sec | 17 (all) |
| Scale | 12B CUs | 1B | 1,250 CUs/sec | 17 (all) |
| Enterprise | Custom CUs | Custom | Custom CUs/sec | 17 (all) |
CUPs (Compute Units Per Second): Maximum throughput rate. Average request = ~25 CUs, so 50 CUPs ≈ 2 requests/sec.
Monthly CU Pool: Total compute units available per month. Resets on billing cycle.
Trace CU Pool: Separate allocation for trace_* and debug_* methods (high-compute operations).
Understanding CU-Based Rate Limiting
Axol uses Compute Units (CUs) to measure API usage. This provides fair pricing where simple calls cost less than complex operations.
Two Types of Limits
- Throughput (CUPs): Maximum CUs per second. Controls request rate.
- Monthly CU Pool: Total CUs available per billing cycle. Controls volume.
Both limits work together:
- CUPs prevent bursts that could overload infrastructure
- Monthly pool ensures fair usage across billing period
Standard vs Trace CU Pools
Trace and debug methods consume 50-5000x more compute than standard calls. To prevent accidental overages, they use a separate CU pool:
- Standard Pool: eth_, net_, web3_*, WebSocket subscriptions
- Trace Pool: trace_, debug_ methods (Growth+ tiers only)
Monitoring Usage
Response Headers
Every API response includes rate limit headers:
X-RateLimit-CU-Limit: 2000000000
X-RateLimit-CU-Remaining: 1950000000
X-RateLimit-CU-Reset: 1704153600
X-RateLimit-Trace-CU-Limit: 100000000
X-RateLimit-Trace-CU-Remaining: 95000000
X-RateLimit-Trace-CU-Reset: 1704153600
Dashboard Monitoring
Access real-time usage at app.axol.io/dashboard:
- Current month CU usage (standard + trace)
- Projected overage cost
- Historical usage charts
- Cost breakdown by method
Handling Rate Limit Errors
429 Too Many Requests
When you exceed CUPs (throughput limit):
{
"error": "Rate limit exceeded",
"retryAfter": 2,
"cupsLimit": 330,
"currentCups": 350
}
Solution: Implement exponential backoff
import time
from axol import AxolClient, RateLimitError
client = AxolClient(api_key="YOUR_KEY")
def with_retry(func, *args, max_retries=3):
for attempt in range(max_retries):
try:
return func(*args)
except RateLimitError as e:
if attempt == max_retries - 1:
raise
wait_time = min(2 ** attempt, e.retry_after)
print(f"Rate limited. Waiting {wait_time}s...")
time.sleep(wait_time)
# Usage
result = with_retry(client.eth_call, "ethereum", {...})
402 CU Pool Exhausted
When you exceed monthly CU pool:
{
"error": "Monthly CU pool exhausted",
"cuLimit": 2000000000,
"cuUsed": 2000000000,
"resetDate": "2024-02-01T00:00:00Z",
"upgradeUrl": "https://app.axol.io/upgrade"
}
Options:
- Wait until billing cycle resets
- Enable overage billing (charged at $0.42/1M CUs)
- Upgrade to higher tier
Usage Alerts
Automatic email alerts sent at:
- 80% usage: Warning notification
- 90% usage: Urgent notification
- 100% usage: Critical notification + throttling begins
- 110% usage: Hard limit (or overage billing if enabled)
Configure alert preferences at app.axol.io/settings/alerts.
Optimization Tips
1. Cache Responses
Many blockchain queries return static data. Cache locally:
from functools import lru_cache
@lru_cache(maxsize=1000)
def get_block_cached(chain, block_number):
return client.get_block(chain, block_number)
2. Use Batch Endpoints
Batch multiple queries into single request:
# Bad: 100 separate requests = 100 * 15 = 1,500 CUs
balances = [client.eth_get_balance(addr) for addr in addresses]
# Good: 1 batch request = 100 * 15 = 1,500 CUs (same cost, but faster)
balances = client.batch_get_balances(addresses)
3. Avoid Trace Methods When Possible
| Method | CU Cost | Alternative | CU Cost |
|---|---|---|---|
debug_traceTransaction | 5,000 | eth_getTransactionReceipt | 15 |
trace_block | 2,500 | eth_getBlockReceipts | 250 |
Use trace methods only when you need detailed execution traces.
4. Optimize Log Queries
eth_getLogs cost scales with block range:
# Bad: Wide range = 500 CUs
logs = client.eth_get_logs(from_block=0, to_block="latest")
# Good: Narrow range = 75 CUs
logs = client.eth_get_logs(from_block=19000000, to_block=19001000)
5. Monitor High-CU Methods
Track which methods consume most CUs:
import logging
logging.basicConfig(level=logging.INFO)
# Logs CU cost per request
client = AxolClient(api_key="YOUR_KEY", log_cu_usage=True)
Troubleshooting
High CU Usage
If you're consuming CUs faster than expected:
- Check method distribution: Are you using trace methods?
- Review log queries: Wide block ranges inflate costs
- Audit batch sizes: Extremely large batches may hit limits
- Check WebSocket subscriptions: Each event costs 40 CUs
Trace Pool Exhaustion
If you exhaust trace CU pool before standard pool:
- Optimize trace usage: Use only when necessary
- Cache trace results: Traces for same tx don't change
- Consider Scale tier: 10x larger trace pool (1B vs 100M CUs)
- Use debug methods selectively:
debug_traceTransactioncosts 5,000 CUs
Unexpected Throttling
If you're throttled despite available CUs:
- Check CUPs limit: You may hit throughput before volume limit
- Spread requests over time: Don't burst all requests at once
- Implement rate limiting: Client-side throttling prevents 429s
from axol import AxolClient
import time
client = AxolClient(api_key="YOUR_KEY")
# Respect CUPs limit (e.g., 330 CUPs for Growth tier)
cups_limit = 330
avg_cu_per_request = 25
max_requests_per_second = cups_limit / avg_cu_per_request # ~13 req/s
for i in range(100):
client.eth_block_number("ethereum")
if i % max_requests_per_second == 0:
time.sleep(1) # Throttle to stay under limit
Next Steps
- Pricing - Full CU cost breakdown
- Authentication - API key management
- WebSockets - Real-time streaming costs