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.

json
{ "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
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

javascript
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));
  }
}
python
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 answer 200 within 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.