All API requests authenticated via API key or OAuth token are subject to rate limiting. Limits are enforced per organization — every API key and OAuth token issued to your organization shares one rate-limit budget. Creating additional credentials does not raise your limit.
| Authentication method | Header | Applies to |
|---|
| API key | x-auth-apikey | Developer and Partner Developer accounts |
| OAuth token | x-auth-access-token | OAuth-authorized applications |
How limits work
Rhombus uses a token bucket algorithm. Two values govern your throughput:
- A sustained refill rate (requests per second) — your steady-state ceiling.
- A burst capacity, roughly 10× the refill rate by default — headroom that absorbs short spikes.
Bursts succeed instantly while the bucket has tokens. Once the bucket drains, sustained traffic above the refill rate returns 429s until the bucket refills.
Default rates apply to all organizations. To request a higher limit, contact Rhombus support.
Throttled responses
When your request is rate limited, the API returns a 429 status with a Retry-After header:
HTTP/1.1 429 Too Many Requests
Retry-After: 1
Content-Type: text/plain
Too many api requests. Enhance your calm.
| Detail | Value |
|---|
| Status code | 429 |
Retry-After header | Seconds to wait before retrying. Computed from your organization’s refill rate; always at least 1 second. |
| Body | Too many api requests. Enhance your calm. |
Do not immediately retry on a 429. Repeated requests while rate limited continue to be rejected and do not reset your limit window.
Handling rate limits
Read the Retry-After header
The Retry-After header tells you exactly how many seconds to wait, computed from your organization’s refill rate. Always prefer this value over hardcoded delays.
Pause requests
Stop sending requests for the duration specified in the header.
Retry your request
After the wait period, retry the original request.
Retry strategy
Use exponential backoff with jitter for the most resilient integration:
delay = min(base_delay × 2^attempt + random_jitter, max_delay)
| Parameter | Recommended value |
|---|
| Base delay | 1 second |
| Max delay | 60 seconds |
| Max retry attempts | 5–10 |
| Jitter | Random 0–1 second |
import time
import random
import requests
def call_api(url, headers, payload, max_retries=5):
for attempt in range(max_retries):
response = requests.post(url, headers=headers, json=payload)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
wait = int(response.headers.get("Retry-After", 60))
time.sleep(wait)
continue
if response.status_code >= 500:
delay = min(1 * (2 ** attempt) + random.random(), 60)
time.sleep(delay)
continue
# 4xx errors (other than 429) are not retryable
response.raise_for_status()
raise Exception("Max retries exceeded")
# Usage
result = call_api(
url="https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList",
headers={
"x-auth-scheme": "api-token",
"x-auth-apikey": "YOUR_API_KEY",
"Content-Type": "application/json"
},
payload={}
)
async function callApi(url, headers, payload, maxRetries = 5) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json", ...headers },
body: JSON.stringify(payload),
});
if (response.ok) {
return await response.json();
}
if (response.status === 429) {
const wait = parseInt(response.headers.get("Retry-After") || "60", 10);
await new Promise((r) => setTimeout(r, wait * 1000));
continue;
}
if (response.status >= 500) {
const delay = Math.min(1 * 2 ** attempt + Math.random(), 60);
await new Promise((r) => setTimeout(r, delay * 1000));
continue;
}
// 4xx errors (other than 429) are not retryable
throw new Error(`Request failed: ${response.status} ${response.statusText}`);
}
throw new Error("Max retries exceeded");
}
// Usage
const result = await callApi(
"https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList",
{
"x-auth-scheme": "api-token",
"x-auth-apikey": "YOUR_API_KEY",
},
{}
);
#!/bin/bash
URL="https://api2.rhombussystems.com/api/camera/getMinimalCameraStateList"
API_KEY="YOUR_API_KEY"
MAX_RETRIES=5
for attempt in $(seq 0 $((MAX_RETRIES - 1))); do
HTTP_CODE=$(curl -s -o /tmp/response.json -D /tmp/response.headers -w "%{http_code}" \
-X POST "$URL" \
-H "Content-Type: application/json" \
-H "x-auth-scheme: api-token" \
-H "x-auth-apikey: $API_KEY" \
-d '{}')
if [ "$HTTP_CODE" -eq 200 ]; then
cat /tmp/response.json
exit 0
elif [ "$HTTP_CODE" -eq 429 ]; then
RETRY_AFTER=$(awk 'tolower($1) == "retry-after:" { gsub(/[^0-9]/, "", $2); print $2; exit }' /tmp/response.headers)
: "${RETRY_AFTER:=60}"
echo "Rate limited. Waiting ${RETRY_AFTER} seconds..." >&2
sleep "$RETRY_AFTER"
elif [ "$HTTP_CODE" -ge 500 ]; then
DELAY=$(echo "1 * 2^$attempt" | bc)
[ "$DELAY" -gt 60 ] && DELAY=60
echo "Server error ($HTTP_CODE). Retrying in ${DELAY}s..." >&2
sleep "$DELAY"
else
echo "Client error ($HTTP_CODE). Not retrying." >&2
cat /tmp/response.json >&2
exit 1
fi
done
echo "Max retries exceeded." >&2
exit 1
When to retry
| Response | Action |
|---|
200–299 | Success — no retry needed |
429 | Wait for Retry-After seconds, then retry |
5xx | Transient server error — retry with exponential backoff |
4xx (not 429) | Client error — fix the request, do not retry |
The rate limiting system is fail-open. If the rate-limit service or its backing store is unavailable, your request is allowed through. Do not rely on this behavior — always design your integration to respect limits.
What changed
Recent rate-limiter improvements are now live:
- Limits are now pooled across all of an organization’s credentials. Distributing traffic across multiple API keys no longer increases throughput.
- Short bursts above your sustained rate are now allowed.
Retry-After reflects the true wait time computed from your refill rate, instead of a fixed 60 seconds.
Alert notification throttling
Alert notifications (distinct from API rate limits) have a configurable minimum interval between consecutive alerts of the same type for a given device. This prevents alert fatigue from high-frequency events like motion detection.
| Interval | Seconds |
|---|
| 1 minute | 60 |
| 2 minutes | 120 |
| 5 minutes (default) | 300 |
| 10 minutes | 600 |
| 20 minutes | 1200 |
| 30 minutes | 1800 |
| 1 hour | 3600 |
Configure this per policy and per activity type. During the backoff window, duplicate alerts for the same device and activity type are suppressed server-side.
Alerts with new identity information — such as a different recognized face or license plate — bypass the backoff and are delivered immediately, even within the suppression window.