Errors & Status Codes
The DFIR Suite API uses conventional HTTP status codes and returns structured JSON error bodies so you can handle failures programmatically.
Error Response Format
Every error response follows a consistent JSON structure:
{
"error": {
"type": "authentication_error",
"code": "api_key_invalid",
"message": "The API key provided is not valid."
}
}HTTP Status Codes
| Status | Meaning | Retry? |
|---|---|---|
| 200 | Success | — |
| 400 | Bad Request — invalid input | No, fix request |
| 401 | Unauthorized — invalid/missing key | No, check key |
| 402 | Payment Required — insufficient credits | No, add credits |
| 403 | Forbidden — wrong permissions or plan | No, upgrade |
| 429 | Rate Limited | Yes, after Retry-After |
| 500 | Internal Error | Yes, with backoff |
| 503 | Service Unavailable | Yes, with backoff |
Error Types
authentication_errorAPI key issues — missing, invalid, expired, or revoked.
authorization_errorPermission or plan issues — the key lacks access to the requested resource.
invalid_requestBad input — missing fields, wrong types, or malformed request body.
rate_limit_exceededToo many requests — you have exceeded your rate limit window.
insufficient_creditsOut of credits — top up your account to continue making requests.
internal_errorServer error — something went wrong on our end. Safe to retry with backoff.
service_unavailableUpstream down — a dependent service is temporarily unavailable.
Common Error Codes
| Code | Type | Message |
|---|---|---|
| api_key_missing | authentication_error | API key is required |
| api_key_invalid | authentication_error | The API key is invalid |
| api_key_expired | authentication_error | API key has expired |
| api_key_revoked | authentication_error | API key has been revoked |
| insufficient_permissions | authorization_error | Key lacks required permission |
| plan_required | authorization_error | Feature requires higher plan |
| insufficient_credits | insufficient_credits | Not enough credits |
| rate_limit_exceeded | rate_limit_exceeded | Rate limit exceeded |
Handling Errors
Always check the HTTP status code and parse the error body to decide whether to retry or surface the error to the user.
import requests
import time
def analyze_email(raw_email: str, api_key: str, max_retries: int = 3):
url = "https://dfir-lab.ch/api/v1/phishing/analyze"
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
for attempt in range(max_retries):
resp = requests.post(url, json={"raw_email": raw_email}, headers=headers)
if resp.status_code == 200:
return resp.json()
error = resp.json().get("error", {})
# Don't retry client errors (except rate limits)
if resp.status_code == 429:
retry_after = int(resp.headers.get("Retry-After", 5))
time.sleep(retry_after)
continue
if resp.status_code in (500, 503):
time.sleep(2 ** attempt) # exponential backoff
continue
# 400, 401, 402, 403 — not retryable
raise Exception(
f"API error {resp.status_code}: "
f"[{error.get('code')}] {error.get('message')}"
)
raise Exception("Max retries exceeded")async function analyzeEmail(rawEmail, apiKey, maxRetries = 3) {
const url = "https://dfir-lab.ch/api/v1/phishing/analyze";
for (let attempt = 0; attempt < maxRetries; attempt++) {
const resp = await fetch(url, {
method: "POST",
headers: {
Authorization: `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ raw_email: rawEmail }),
});
if (resp.ok) return resp.json();
const { error } = await resp.json();
// Rate limited — wait and retry
if (resp.status === 429) {
const retryAfter = parseInt(resp.headers.get("Retry-After") || "5");
await new Promise((r) => setTimeout(r, retryAfter * 1000));
continue;
}
// Server errors — exponential backoff
if (resp.status >= 500) {
await new Promise((r) => setTimeout(r, 2 ** attempt * 1000));
continue;
}
// Client errors — not retryable
throw new Error(
`API error ${resp.status}: [${error?.code}] ${error?.message}`
);
}
throw new Error("Max retries exceeded");
}