Errors, retries and limits
What to do with each kind of failure, what a retry costs, and how an Idempotency-Key makes it safe.
What to retry
- 429 busy — the engine is at capacity. Wait the
Retry-Afterseconds and send the same request again. A refused request is never charged. - 402 quota_exceeded — the account's monthly quota is used up.
Retry-Aftercounts down to 00:00 UTC on the 1st of next month; a retry before that fails the same way. Nothing was charged. - 503 service_unavailable — an engine is not available on this deployment (the nesting engine, or the async
maxtier). ReadGET /v1/health(modes,nestEngines,maxEngines) instead of retrying in a loop. - 504 — the solve ran out of time. On the rectangular paths the proxy answers and the body is not the JSON error envelope; a call the proxy gave up on is not charged. On nesting the engine answers
solve_timeout. The same job will most likely time out again: split it, or send it toengine: "max"(asynchronous, see async max jobs). - 409 idempotency_in_progress — you sent an
Idempotency-Keyand the first request with it is still running. Wait theRetry-Aftersecond and send the same request again: you receive its answer. - 409 idempotency_conflict — the key was already used for a different request. Do not retry: send the new job with a new key.
- 400, 401, 403, 404, 413 — the request or the key is wrong. Retrying the same request returns the same error: fix it first. Validation runs before a call is reserved, so a 400 never costs a call.
What a retry costs
Without an idempotency key, a repeated request is a new call: the engines are deterministic, so it returns the same plan, and it is charged again. Keep the answer instead of re-solving it; engineVersion tells you when a cached plan was made by an older algorithm.
Retrying safely: Idempotency-Key
When a connection drops, you cannot tell whether the request arrived. Send an Idempotency-Key header with every optimize call — a value you generate per job, 8–128 characters of A-Z a-z 0-9 _ - (a UUID works) — and retry with the same key:
- Within 24 hours, the same key with the same request (method, path and body bytes) returns the stored answer — the same status and the same body — with the header
Idempotency-Replayed: true. It is not charged and not solved again. - With
engine: "max", the retry returns the samejobIdinstead of queuing a second job. - Only a delivered success is stored. After an error or a refusal (a 4xx, 429 or 5xx), a retry with the same key runs again.
- Keys belong to your account, separately for test and live keys; the public demo key ignores the header.
KEY_ID=$(uuidgen) # once per job — reuse it for every retry of that job
curl -s https://api.cutoptim.com/v1/optimize/2d \
-H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
-H "Idempotency-Key: $KEY_ID" \
-d '{"parts":[{"w":600,"h":400,"qty":4}],"stock":[{"w":2440,"h":1220}]}'Send the body exactly as the first time: the comparison is on the bytes, so re-serialising the JSON with another key order counts as a different request (409 idempotency_conflict).
To check a request before spending a call, send the same body to POST /v1/validate/2d (or /1d, /wood, /nest): free, no key, no quota, and the same verdict the optimize path would give.
Staying inside the limits
The input caps are on the limits page, every error code with its meaning on the errors page. On every successful optimize, X-RateLimit-Remaining says how many calls the account has left this month — read it rather than counting yourself.