Error Handling
HTTP status codes, error response format, and troubleshooting guide.
Error Handling
The InkScan API uses standard HTTP status codes and returns structured error responses.
Error response format
All errors return a consistent JSON structure:
{
"error": {
"code": "invalid_image",
"message": "The uploaded file is not a supported image format.",
"status": 400
}
}
Status codes
| Code | Meaning | When it happens |
|---|---|---|
200 |
Success | Recognition completed |
400 |
Bad Request | Invalid parameters, unsupported format, or missing image |
401 |
Unauthorized | Missing or invalid API key |
402 |
Payment Required | Insufficient credits for this request |
413 |
Payload Too Large | Image exceeds 10 MB or PDF exceeds 50 MB |
422 |
Unprocessable | Image is valid but recognition failed (e.g. blank page, no text detected) |
429 |
Rate Limited | Too many requests. Check Retry-After header |
500 |
Server Error | Something went wrong on our side. Retry with exponential backoff |
Error codes
| Error code | Description |
|---|---|
invalid_image |
File is not a supported image format (JPG, PNG, WebP) |
image_too_large |
Image exceeds maximum file size |
no_text_detected |
Image was processed but no handwriting was found |
low_quality |
Image quality too low for reliable recognition |
insufficient_credits |
Not enough credits to process this request |
invalid_api_key |
API key is missing, malformed, or revoked |
Rate limits
Rate limits are per API key, regardless of credit balance:
| Tier | Requests per minute |
|---|---|
| Default | 60 |
Rate limited responses include a Retry-After header (in seconds).
Retry strategy
For 429 and 5xx errors, use exponential backoff:
import time, base64, requests
def recognize_with_retry(image_path, max_retries=3):
with open(image_path, "rb") as f:
image_base64 = base64.b64encode(f.read()).decode()
for attempt in range(max_retries):
response = requests.post(
"https://api.inkscan.app/v1/recognize",
headers={"Authorization": f"Bearer {API_KEY}"},
json={"image_base64": image_base64},
)
if response.status_code == 200:
return response.json()
if response.status_code in (429, 500, 502, 503):
wait = 2 ** attempt
retry_after = response.headers.get("Retry-After")
if retry_after:
wait = int(retry_after)
time.sleep(wait)
continue
# Non-retryable error
response.raise_for_status()
raise Exception("Max retries exceeded")