When to Retry
| Status Code | Retry? | Action |
|---|---|---|
400, 422 | No | Fix the request — these are request errors (bad payload, missing fields) |
401 | Once | Refresh your JWT token via /auth/token, then retry the original request |
500, 503 | Yes | Transient server error — retry with exponential backoff |
| Network timeout | Yes | Retry with exponential backoff, check /health before retrying |
Retry Strategy
Use exponential backoff for transient errors (500, 503). Do not retry request errors (400, 401, 422) — fix the request first.| Attempt | Wait | Cumulative |
|---|---|---|
| 1st retry | 1 second | 1s |
| 2nd retry | 2 seconds | 3s |
| 3rd retry | 4 seconds | 7s |
Timeout Guidance
| Request Type | Recommended Timeout |
|---|---|
/evaluate | 120 seconds |
/health | 10 seconds |
Fallback: Route to Manual Review
If the API is unresponsive after all retries, route the record to your manual review queue. Before retrying, call/health to confirm service availability.
Code Examples
import requests
import time
def evaluate_with_retry(base_url, api_key, record_data, max_retries=3):
token = get_token(base_url, api_key)
for attempt in range(max_retries):
try:
response = requests.post(
f"{base_url}/evaluate",
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
json={"record": record_data},
timeout=120
)
if response.status_code == 200:
return response.json()
if response.status_code == 206:
return response.json()
if response.status_code == 401:
token = get_token(base_url, api_key)
continue
if response.status_code >= 400 and response.status_code < 500:
raise Exception(f"Client error {response.status_code}: {response.text}")
if response.status_code >= 500:
if attempt < max_retries - 1:
time.sleep(2 ** attempt)
continue
raise Exception(f"Server error after {max_retries} attempts")
except requests.exceptions.Timeout:
if attempt < max_retries - 1:
health = requests.get(f"{base_url}/health", timeout=10)
if health.status_code != 200:
time.sleep(2 ** attempt)
continue
raise
raise Exception("Max retries exceeded — route to manual review queue")
def get_token(base_url, api_key):
response = requests.post(
f"{base_url}/auth/token",
headers={"X-API-Key": api_key}
)
response.raise_for_status()
return response.json()["access_token"]
async function evaluateWithRetry(baseUrl, getToken, recordData, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const token = await getToken();
try {
const response = await fetch(`${baseUrl}/evaluate`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({ record: recordData }),
signal: AbortSignal.timeout(120_000)
});
if (response.ok || response.status === 206) {
return await response.json();
}
if (response.status === 401) {
await getToken({ forceRefresh: true });
continue;
}
if (response.status >= 400 && response.status < 500) {
throw new Error(`Client error ${response.status}: ${await response.text()}`);
}
if (attempt < maxRetries - 1) {
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
continue;
}
throw new Error(`Server error after ${maxRetries} attempts`);
} catch (err) {
if (err.name === "TimeoutError" && attempt < maxRetries - 1) {
const health = await fetch(`${baseUrl}/health`).catch(() => null);
if (!health?.ok) {
await new Promise(r => setTimeout(r, 2 ** attempt * 1000));
continue;
}
}
if (attempt === maxRetries - 1) throw err;
}
}
}
Related
- Error Handling — HTTP status codes and error response formats
- Troubleshooting — Solutions for common issues
- Environments — Timeout and response time guidance
