The Shelfforce API uses conventional HTTP status codes and returns structured error responses with machine-readable error codes.
{
"error": {
"code": "VALIDATION_FAILED",
"message": "The 'imageUrl' field is required.",
"details": {
"field": "imageUrl",
"reason": "required"
}
}
}
| Field | Type | Description |
|---|
code | string | Machine-readable error code (see table below) |
message | string | Human-readable description |
details | object | Optional additional context |
Error codes
Missing Authorization header or invalid format. Include your API key as Authorization: Bearer sf_live_....
Resource does not exist or belongs to a different organisation.
Missing required fields or invalid values. Check the details field.
Rate limit exceeded. Wait for Retry-After seconds. See Rate Limits.
Unexpected server error. Safe to retry with backoff. Contact hey@shelfforce.ai if persistent.
Handling errors
By status code
Node.js
Python
- 4xx — Client errors. Fix the request before retrying. Do not retry
401, 402, 403, or 422.
- 429 — Rate limited. Wait for
Retry-After, then retry with exponential backoff.
- 5xx — Server errors. Safe to retry with exponential backoff.
const response = await fetch("https://shelfforce.ai/api/v1/analyses", {
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ imageUrl }),
});
if (!response.ok) {
const { error } = await response.json();
switch (error.code) {
case "AUTH_REQUIRED":
case "INVALID_API_KEY":
throw new Error(`Authentication failed: ${error.message}`);
case "INSUFFICIENT_CREDITS":
await alertTeam("Credits exhausted", error.message);
throw new Error(error.message);
case "RATE_LIMITED":
const retryAfter = parseInt(
response.headers.get("Retry-After") || "10", 10
);
await sleep(retryAfter * 1000);
return retry();
case "INTERNAL_ERROR":
await sleep(2000);
return retry();
}
}
response = requests.post(
"https://shelfforce.ai/api/v1/analyses",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
},
json={"imageUrl": image_url},
)
if not response.ok:
error = response.json()["error"]
if error["code"] in ("AUTH_REQUIRED", "INVALID_API_KEY"):
raise Exception(f"Auth failed: {error['message']}")
elif error["code"] == "RATE_LIMITED":
retry_after = int(response.headers.get("Retry-After", "10"))
time.sleep(retry_after)
return retry()
elif error["code"] == "INTERNAL_ERROR":
time.sleep(2)
return retry()
Always check the code field for programmatic error handling rather than parsing the message string. Error messages may change, but codes are stable.