Rate Limiting
Quick Reference
WhatA55 API rate limiting policy and retry strategies
WhyAvoid service disruption during high-traffic periods
Reading Time7 min
DifficultyIntermediate
PrerequisitesWorking API integration
Policy
| Parameter | Value |
|---|---|
| Rate limit | 100 requests per minute per API key |
| Window | Rolling 60-second window |
| Scope | Per client_id across all endpoints |
| Response on limit | HTTP 429 Too Many Requests |
Rate Limit Headers
Every API response includes rate limit headers:
HTTP/1.1 200 OK
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 73
X-RateLimit-Reset: 1711036800
Content-Type: application/json
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed per window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | Unix timestamp when the window resets |
HTTP 429 Response
When you exceed the limit:
{
"error": {
"code": "RATE_LIMIT_EXCEEDED",
"message": "Too many requests. Retry after 23 seconds.",
"details": [
{
"field": "rate_limit",
"reason": "100 requests per minute exceeded"
}
]
},
"request_id": "req_abc123..."
}
The Retry-After header indicates how many seconds to wait:
HTTP/1.1 429 Too Many Requests
Retry-After: 23
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1711036823
Retry Strategy with Exponential Backoff
- cURL
- Python
- Node.js
#!/bin/bash
MAX_RETRIES=3
RETRY_DELAY=1
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(curl -s -w "\n%{http_code}" \
-X POST "https://sandbox.api.a55.tech/api/v1/bank/wallet/charge/" \
-H "Authorization: Bearer ${A55_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
-d '{"amount": "100.00", "currency": "BRL"}')
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | head -n -1)
if [ "$HTTP_CODE" -ne 429 ] && [ "$HTTP_CODE" -lt 500 ]; then
echo "$BODY"
exit 0
fi
echo "Attempt $i failed (HTTP $HTTP_CODE). Retrying in ${RETRY_DELAY}s..."
sleep $RETRY_DELAY
RETRY_DELAY=$((RETRY_DELAY * 2))
done
echo "All retries exhausted"
exit 1
import os
import time
import random
import requests
def request_with_backoff(method, url, max_retries=3, **kwargs):
headers = {
"Authorization": f"Bearer {os.environ['A55_ACCESS_TOKEN']}",
**kwargs.pop("headers", {}),
}
for attempt in range(max_retries + 1):
try:
response = requests.request(method, url, headers=headers, **kwargs)
remaining = response.headers.get("X-RateLimit-Remaining")
if remaining and int(remaining) < 10:
print(f"Warning: {remaining} requests remaining in window")
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", 2 ** attempt))
jitter = random.uniform(0, 1)
time.sleep(retry_after + jitter)
continue
if response.status_code >= 500:
delay = (2 ** attempt) + random.uniform(0, 1)
time.sleep(delay)
continue
return response
except requests.ConnectionError:
if attempt == max_retries:
raise
time.sleep(2 ** attempt)
raise Exception(f"Request failed after {max_retries} retries")
async function requestWithBackoff(method, url, options = {}, maxRetries = 3) {
const headers = {
Authorization: `Bearer ${process.env.A55_ACCESS_TOKEN}`,
"Content-Type": "application/json",
...options.headers,
};
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
const res = await fetch(url, { method, headers, ...options });
const remaining = res.headers.get("X-RateLimit-Remaining");
if (remaining && parseInt(remaining) < 10) {
console.warn(`Warning: ${remaining} requests remaining in window`);
}
if (res.status === 429) {
const retryAfter = parseInt(res.headers.get("Retry-After") || 2 ** attempt);
const jitter = Math.random();
await new Promise(r => setTimeout(r, (retryAfter + jitter) * 1000));
continue;
}
if (res.status >= 500) {
const delay = (2 ** attempt + Math.random()) * 1000;
await new Promise(r => setTimeout(r, delay));
continue;
}
return res;
} catch (err) {
if (attempt === maxRetries) throw err;
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
}
}
throw new Error(`Request failed after ${maxRetries} retries`);
}
Best Practices
- Read rate limit headers — Track
X-RateLimit-Remainingand slow down before hitting zero - Never retry 4xx errors (except
429) — Client errors indicate a bug in your request, not a transient failure - Add jitter to backoff — Prevents thundering herd when multiple clients retry simultaneously
- Use a request queue — Smooth out traffic spikes by queuing requests and draining at a controlled rate
- Cache where possible — Cache GET responses (wallet details, charge status) to reduce request volume
- Batch operations — If you need to create many charges, space them across the window rather than sending all at once