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": "Invalid API key format. Keys must start with 'sk-dfir-'.",
"request_id": "req_abc12345-6789-0123-4567-89abcdef0123"
}
}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 |
| 413 | Payload Too Large — request body exceeds size limit | No, reduce payload |
| 415 | Unsupported Media Type — Content-Type must be application/json | No, fix Content-Type |
| 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 |
|---|---|---|
| missing_authorization | authentication_error | Missing Authorization header |
| invalid_authorization_format | authentication_error | Authorization header must use the Bearer scheme |
| api_key_invalid | authentication_error | Invalid API key format |
| key_expired | authentication_error | This API key has expired |
| key_revoked | authentication_error | This API key has been revoked |
| validation_service_unavailable | authentication_error | Unable to validate API key — try again later |
| insufficient_permissions | authorization_error | Key lacks the required permission |
| plan_insufficient | authorization_error | Endpoint requires a higher plan |
| insufficient_credits | insufficient_credits | Not enough credits |
| invalid_content_type | invalid_request | Content-Type must be application/json |
| payload_too_large | invalid_request | Request body exceeds maximum size |
| invalid_json | invalid_request | Request body is not valid JSON |
| 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://api.dfir-lab.ch/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://api.dfir-lab.ch/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");
}