Errors and retries
Every error is JSON with one field, error, and an HTTP status. Base your code on the status. The message is for people and logs.
{ "error": "image could not be read as a JPEG, PNG or WebP photo." }What each status means
| Status | Meaning | Send it again? |
|---|---|---|
400 |
The body is not a JSON object. | No. Fix the request. |
401 |
The API key is missing or invalid. | No. |
403 |
The key is valid but your plan does not include this endpoint, or the demo key was used. | No. |
413 |
The photo is too large. | No. Send it smaller. |
422 |
A field is wrong, or the photo could not be fetched or read. The message names the field. | No, unless the message says the photo could not be fetched and the URL may have been briefly down. |
429 |
You are over your quota or your requests-per-minute limit. | Yes, after Retry-After seconds. |
500 |
Something failed on our side. | Yes, with a delay. |
502 |
The model failed on this request. | Yes. |
503 |
The model is starting or busy. | Yes, after Retry-After seconds. |
A request that ends in 5xx does not use quota. A request that ends in 413 or 422 does: it was received and checked. A 400, 401, 403 or 429 does not.
When the model is starting
A model that has not been called for a while can need time to be ready. Usually you never see this. When it takes longer, the endpoint waits up to 25 seconds and then answers:
HTTP/1.1 503 Service Unavailable
Retry-After: 5
{ "error": "The model is starting. Try again in a few seconds." }The model keeps starting after that answer, so a retry a few seconds later usually succeeds. It can take a minute or two to be ready, so retry several times before giving up.
A retry loop
async function segment(body, { attempts = 8 } = {}) {
for (let attempt = 1; ; attempt++) {
const response = await fetch('https://stylor.ai/api/v2/models/segment', {
method: 'POST',
headers: { Authorization: `Bearer ${STYLOR_API_KEY}`, 'Content-Type': 'application/json' },
body: JSON.stringify(body),
});
if (response.ok) return response.json();
const retryable = [429, 500, 502, 503].includes(response.status);
if (!retryable || attempt === attempts) {
const { error } = await response.json().catch(() => ({}));
throw new Error(`segment failed (${response.status}): ${error || 'no message'}`);
}
const wait = Number(response.headers.get('Retry-After')) || Math.min(30, 2 ** attempt);
await new Promise((resolve) => setTimeout(resolve, wait * 1000));
}
}import time, requests
def segment(body, attempts=8):
for attempt in range(1, attempts + 1):
r = requests.post(
"https://stylor.ai/api/v2/models/segment",
headers={"Authorization": f"Bearer {STYLOR_API_KEY}"},
json=body,
timeout=60,
)
if r.ok:
return r.json()
if r.status_code not in (429, 500, 502, 503) or attempt == attempts:
r.raise_for_status()
time.sleep(float(r.headers.get("Retry-After") or min(30, 2 ** attempt)))Set your own request timeout to at least 40 seconds. The endpoint can take up to 10 seconds to fetch a photo from a URL and up to 25 waiting for the model.
Photo problems
Most 422 answers are about the photo. The common ones:
image could not be fetched.The URL did not answer200within 10 seconds, redirected, or is not reachable from the public internet. Signed URLs that redirect will fail: send the final URL or a data URL.image could not be read as a JPEG, PNG or WebP photo.The file is another format (HEIC, GIF, AVIF, SVG), is damaged, or is over 40 million pixels. Convert it to JPEG first.image is too small. The shorter edge is under 64 pixels, or the photo is more than 64 times longer than it is wide.image is too large.Over 12 MB by URL or 8 million characters as a data URL. Resize to about 2048 pixels on the long side; the model does not use more.