Errors and request ids
Every failed request answers with an HTTP status code and a JSON body. Every body carries the same top-level fields, whichever endpoint produced it, so one error handler covers the whole API.
Request ids
Every response, success or error, carries an X-Request-Id header. Error bodies repeat it as request_id. Log it with your own records and include it when writing to support@animaldetect.com: it lets us find the exact request, its parameters and its result. On a successful detection the request id is the same value as the result id.
Fields on every error
Machine-readable reason, always at the top level. One of the codes in the table below.
Error family: invalid_request_error, authentication_error, permission_error, billing_error, not_found_error, idempotency_error, rate_limit_error, processing_error or api_error. Branch on it when you do not need the exact code.
Identifies this HTTP exchange. It is also sent as the X-Request-Id header on every response, and on a successful detection it equals the result id. Quote it when writing to support@animaldetect.com.
Link to the row for this code on this page.
Present only when one request field is at fault, for example threshold or Idempotency-Key.
Two body shapes, same fields
Detection, filter, OCR and job endpoints return a flat body where error is the message string. The reference endpoints (species, countries, models) and GET /detect/{id} return a nested body where error is an object. Both shapes stay as they are for existing integrations; both carry the top-level fields above. Read code from the top level and you never need to know which shape you got.
{
"error": "Request validation failed",
"code": "VALIDATION_ERROR",
"details": {
"errors": [
{ "field": "threshold", "message": "Threshold must be between 0.01 and 0.99" }
]
},
"type": "invalid_request_error",
"request_id": "1d6492ab-f29e-480e-ae56-2133f3fb5429",
"doc_url": "https://www.animaldetect.com/doc/api/errors#VALIDATION_ERROR",
"param": "threshold"
}{
"error": {
"code": "NOT_FOUND",
"message": "Detection result with ID \"...\" was not found"
},
"code": "NOT_FOUND",
"type": "not_found_error",
"request_id": "7c1b0d2e-5e5a-4d0b-9f0e-3f9a4c1e8b21",
"doc_url": "https://www.animaldetect.com/doc/api/errors#NOT_FOUND"
}import requests
response = requests.post(
"https://api.animaldetect.com/v1/detect",
headers={
"Authorization": "Bearer " + API_KEY,
"User-Agent": "MyApp/1.0 (ops@example.com)",
},
files={"image": open("camera-trap.jpg", "rb")},
)
if response.status_code == 429:
wait_seconds = int(response.headers.get("Retry-After", "30"))
elif not response.ok:
error = response.json()
# error["type"] is the family, error["code"] the exact reason,
# error["param"] the field at fault when there is one.
print(error["code"], error["request_id"], error["doc_url"])Error types
Error codes
The doc_url in an error body points at the matching row here.
Retries
Retry 429 after the number of seconds in Retry-After, and 503 with exponential backoff. Never retry a 400, 401, 402 or 409 with the same payload. When you retry a detection, send the same Idempotency-Key so a request that did succeed is never billed twice.