Rate Limiting¶
Openbridge APIs enforce rate limits to ensure fair usage and service stability. When a limit is exceeded the API returns 429 Too Many Requests. Clients must handle this response and back off before retrying.
Limits¶
| Scope | Limit | Window |
|---|---|---|
| Per IP address | 1,000 requests | 5 minutes (rolling) |
Response Headers¶
Rate-limit headers are only present on 429 responses:
| Header | Value | Description |
|---|---|---|
Retry-After |
60 |
Seconds to wait before retrying |
No per-request quota headers (X-RateLimit-Remaining, etc.) are included in normal responses.
Error Response¶
A rate-limited request returns HTTP 429 with a JSON body:
{
"errors": [
{
"status": "429",
"title": "Too Many Requests",
"detail": "Rate limit exceeded. Retry after 60 seconds."
}
]
}
Retry Strategy¶
Do not retry immediately after receiving a 429. Wait at least the Retry-After value (60 seconds), then apply exponential backoff with jitter for subsequent failures.
Recommended algorithm¶
- On
429, wait 60 seconds (Retry-Afterheader value). - For each subsequent failure, double the wait time up to a maximum of 5 minutes.
- Add random jitter (±10–20%) to avoid synchronized retries from multiple clients.
- After a configurable number of retries (recommended: 5), surface the error to the caller.
Example — Python¶
import time
import random
import requests
def request_with_backoff(method, url, **kwargs):
max_retries = 5
wait = 60 # matches Retry-After
for attempt in range(max_retries):
response = requests.request(method, url, **kwargs)
if response.status_code != 429:
return response
jitter = wait * random.uniform(0.1, 0.2)
sleep_time = wait + jitter
if attempt < max_retries - 1:
time.sleep(sleep_time)
wait = min(wait * 2, 300) # cap at 5 minutes
else:
response.raise_for_status()
return response
Example — JavaScript (Node.js)¶
async function requestWithBackoff(url, options = {}, maxRetries = 5) {
let wait = 60; // matches Retry-After
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, options);
if (response.status !== 429) {
return response;
}
if (attempt === maxRetries - 1) {
throw new Error(`Rate limit exceeded after ${maxRetries} retries`);
}
const jitter = wait * (0.1 + Math.random() * 0.1);
const sleepMs = (wait + jitter) * 1000;
await new Promise((resolve) => setTimeout(resolve, sleepMs));
wait = Math.min(wait * 2, 300);
}
}
Best Practices¶
Do not retry on other 4xx errors. Retry logic should apply only to 429 and transient 5xx responses. Retrying 400, 401, 403, or 404 will not resolve those errors.
Serialize bulk operations. When creating multiple subscriptions or triggering multiple history requests, introduce a small delay between calls (e.g., 100–200 ms) rather than firing them all concurrently.
Cache access tokens. The Authentication API is rate-limited like all other APIs. Reuse JWT access tokens until they expire rather than exchanging the refresh token on every request. See Authentication API for token lifetime details.
Use idempotent requests safely. If a POST request is interrupted before a response is received (not a 429), look up whether the resource was created before retrying to avoid duplicates.
Summary¶
| Scenario | Action |
|---|---|
429 received |
Wait 60 seconds, then retry with exponential backoff |
Repeated 429 after retries |
Surface the error; do not retry indefinitely |
Other 4xx errors |
Do not retry; inspect and fix the request |