Error Handling¶
This guide covers how to handle errors when integrating with the XLSX API.
Error Response Format¶
All errors return a JSON object with a detail field:
HTTP Status Codes¶
| Code | Meaning | Action |
|---|---|---|
400 |
Bad request | Fix the request (validation error, missing fields) |
401 |
Unauthorized | Check your API key |
403 |
Forbidden | Check permissions or IP whitelist with your admin |
404 |
Not found | Verify tenant slug, engine slug, or job ID |
429 |
Rate limited | Back off and retry after a delay |
500 |
Server error | Retry with backoff; report if persistent |
Handling by Endpoint¶
Execute (POST /jobs/execute/...)¶
| Error | Cause | Fix |
|---|---|---|
400 validation failed |
Inputs don't match the schema | Check input_schema via the about endpoint |
400 no active version |
Engine has no activated version | Contact your tenant admin |
403 no permission |
Your key lacks can_execute |
Contact your tenant admin |
403 IP not whitelisted |
Your IP is blocked | Contact your tenant admin |
429 rate limited |
Too many requests | Implement backoff |
Job Status (GET /jobs/{job_id})¶
A completed job with status: "failed" is not an HTTP error — it's a normal response indicating the Excel processing failed (e.g. formula error, SharePoint issue).
Info
Error messages shown to consumers are intentionally generic. Detailed errors (like which Excel range caused #DIV/0!) are visible to tenant admins in the dashboard.
Download (GET /jobs/{job_id}/download)¶
The most common error is requesting a download before the job completes:
Always check status == "completed" before requesting a download.
Rate Limiting¶
When you hit a rate limit, the server returns 429. Implement exponential backoff:
import time
def call_with_retry(func, max_retries=3):
delay = 1
for attempt in range(max_retries):
resp = func()
if resp.status_code == 429:
time.sleep(delay)
delay *= 2
continue
return resp
raise Exception("Rate limited after max retries")
Rate Limit Tiers¶
| Scope | Limit |
|---|---|
| Execution endpoints (per API key) | 30 req/min |
| Job endpoints (per IP) | 30 req/min |
| General API (per IP) | 100 req/min |
Recommended Error Handling Pattern¶
import requests
API_BASE = "https://api.xlsxapi.eu"
HEADERS = {"X-API-Key": "YOUR_API_KEY"}
def execute_and_wait(tenant, engine, inputs):
# Execute
resp = requests.post(
f"{API_BASE}/jobs/execute/{tenant}/{engine}",
json={"inputs": inputs},
headers=HEADERS,
)
if resp.status_code == 400:
raise ValueError(f"Invalid input: {resp.json()['detail']}")
elif resp.status_code == 401:
raise PermissionError("Invalid API key")
elif resp.status_code == 403:
raise PermissionError(resp.json()["detail"])
elif resp.status_code == 429:
raise RuntimeError("Rate limited — back off and retry")
resp.raise_for_status()
job_id = resp.json()["job_id"]
# Poll
import time
delay = 1
while True:
time.sleep(delay)
status_resp = requests.get(
f"{API_BASE}/jobs/{job_id}",
headers=HEADERS,
)
status_resp.raise_for_status()
result = status_resp.json()
if result["status"] == "completed":
return result["outputs"]
elif result["status"] == "failed":
raise RuntimeError(
f"Job failed: {result.get('error_message', 'Unknown error')}"
)
delay = min(delay * 2, 15)