Error Handling
The Banklyze API uses conventional HTTP status codes and structured JSON error responses. This guide covers the error format, every error code, and best practices for retries.
Error Response Format
Every error response returns a JSON object with the following fields:
| Name | Type | Required | Description |
|---|---|---|---|
| detail | string | Required | A human-readable description of the error. |
| code | string | Required | A machine-readable error code (see Error Codes table below). |
| errors | array | null | Optional | An array of field-level validation errors. Only present for 422 responses. |
{
"detail": "Deal not found",
"code": "DEAL_NOT_FOUND",
"errors": null
}HTTP Status Codes
The API returns standard HTTP status codes to indicate success or failure:
| Code | Meaning | Description |
|---|---|---|
| 200 | OK | Request succeeded. Response body contains the result. |
| 201 | Created | Resource created successfully. |
| 204 | No Content | Request succeeded with no response body (e.g., DELETE). |
| 400 | Bad Request | The request body or parameters are malformed. |
| 401 | Unauthorized | Missing or invalid API key / session token. |
| 403 | Forbidden | Authenticated but insufficient permissions. |
| 404 | Not Found | The requested resource does not exist. |
| 409 | Conflict | A resource with that identifier already exists. |
| 422 | Unprocessable Entity | Validation failed. Check the errors array for details. |
| 429 | Too Many Requests | Rate limit exceeded. Retry after the Retry-After header. |
| 500 | Internal Server Error | Something went wrong on our end. Please retry. |
Error Codes
The code field uses one of these machine-readable identifiers:
| Error Code | HTTP Status | Description |
|---|---|---|
| VALIDATION_ERROR | 422 | One or more request fields failed validation. |
| AUTH_REQUIRED | 401 | No API key or session token was provided. |
| INVALID_API_KEY | 401 | The API key is invalid, revoked, or expired. |
| FORBIDDEN | 403 | You do not have permission to perform this action. |
| NOT_FOUND | 404 | The requested resource was not found. |
| DEAL_NOT_FOUND | 404 | The specified deal does not exist or is not accessible. |
| DOCUMENT_NOT_FOUND | 404 | The specified document does not exist or is not accessible. |
| DUPLICATE_RESOURCE | 409 | A resource with the same unique identifier already exists. |
| RATE_LIMIT_EXCEEDED | 429 | Too many requests. Back off and retry. |
| QUOTA_EXCEEDED | 429 | Your organization has exceeded its usage quota. |
| PROCESSING_FAILED | 500 | Document processing encountered an unrecoverable error. |
| WEIGHT_SUM_INVALID | 422 | Ruleset factor weights do not sum to 100. |
| INVALID_DECISION | 422 | The decision value is not valid for this deal state. |
Validation Errors
When a request fails validation (HTTP 422), the errors array contains one entry per invalid field. Each entry has a field name and a message describing the constraint that was violated.
{
"detail": "Validation failed",
"code": "VALIDATION_ERROR",
"errors": [
{
"field": "business_name",
"message": "Field required"
},
{
"field": "ein",
"message": "String should match pattern '^\\d{9}$'"
}
]
}errors array when you receive a 422 response. Display field-level messages to your users so they can fix the input.Rate Limiting
The API enforces per-organization rate limits. Every response includes rate limit headers so you can track your usage:
| Name | Type | Required | Description |
|---|---|---|---|
| X-RateLimit-Limit | integer | Required | Maximum requests allowed in the current window. |
| X-RateLimit-Remaining | integer | Required | Requests remaining in the current window. |
| X-RateLimit-Reset | integer | Required | Unix timestamp when the rate limit window resets. |
| Retry-After | integer | Optional | Seconds to wait before retrying (only on 429 responses). |
HTTP/1.1 429 Too Many Requests
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1710350400
Retry-After: 30
{
"detail": "Rate limit exceeded. Try again in 30 seconds.",
"code": "RATE_LIMIT_EXCEEDED",
"errors": null
}Retry Strategy
Use exponential backoff with jitter for reliable retry logic. Only retry on 429 (rate limited) and 5xx (server errors). Never retry 4xx client errors (except 429) since the request will fail again with the same parameters.
#!/bin/bash
MAX_RETRIES=5
DELAY=1
for i in $(seq 1 $MAX_RETRIES); do
RESPONSE=$(curl -s -w "\n%{http_code}" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Org-Id: $ORG_ID" \
"https://api.banklyze.com/v1/deals")
HTTP_CODE=$(echo "$RESPONSE" | tail -1)
BODY=$(echo "$RESPONSE" | sed '$d')
if [ "$HTTP_CODE" -lt 400 ]; then
echo "$BODY"
exit 0
fi
if [ "$HTTP_CODE" -eq 429 ] || [ "$HTTP_CODE" -ge 500 ]; then
echo "Attempt $i failed ($HTTP_CODE). Retrying in ${DELAY}s..."
sleep $DELAY
DELAY=$((DELAY * 2))
continue
fi
# Client error (4xx) — do not retry
echo "Error $HTTP_CODE: $BODY"
exit 1
done
echo "Max retries exceeded"
exit 1429 responses, always prefer the Retry-After header over your calculated backoff delay.