Imagera AI - AI content creation platform for generating images, cloning voices, and enhancing videos. Privacy Policy | Terms

API errors

Every code the Imagera API returns, what causes it, and whether retrying can help.

Read the code, not the status

Errors are RFC 9457 problem documents, served as application/problem+json. Branch on code, which is stable forever — not on the HTTP status, which several codes share. Three codes return 404, two return 409, two return 429 and two return 400.

{
  "type": "https://imagera.ai/developers/errors#invalid_request",
  "title": "Invalid request",
  "status": 422,
  "detail": "A prompt is required for this feature.",
  "code": "invalid_request"
}

type links straight to the section for that code on this page. detail is the only free-text field, and it never names a supplier or an upstream model.

A 401 is deliberately identical for a missing, malformed, unknown, revoked or expired key — distinguishing them would let anyone probe for valid keys. Getting a 403 instead is the signal that your key itself authenticated.

All 27 codes

Ordered by HTTP status.

request_not_ready

HTTP 202Request not ready

The request exists and is still running. This is not a failure — it is the normal answer while a generation is in flight.

What causes it

  • You fetched the result before the generation reached a terminal state.

What to do

Back off and poll the `status_url` again. The response carries `Retry-After`, so you can honour it without parsing prose. Prefer a webhook over polling for anything longer than a few seconds.

Retry with backoff.The identical request may succeed later.

Also carries deprecated_code.

Until 2026-11-17 this also carries `deprecated_code: "request_not_found"`, because these three states were once collapsed into one 404. Read `code`, not `deprecated_code`.

unsupported_parameter

HTTP 400Unsupported parameter

The request parsed and asked for something this version does not honour. Distinct from `malformed_body`, the other 400, which never parsed at all.

What causes it

  • A parameter that exists in the schema but is not served by v1 for this model.

What to do

Remove the parameter. The model document at `/v1/models/{model}` is authoritative for what is accepted.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

malformed_body

HTTP 400Malformed request body

The body could not be parsed at all, so validation never ran. Distinct from `unsupported_parameter`, the other 400, where the body parsed and one field was not honoured.

What causes it

  • Invalid JSON.
  • A missing or wrong `Content-Type` header.

What to do

Send `Content-Type: application/json` and a syntactically valid JSON body.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

idempotency_key_required

HTTP 400Idempotency key required

A board run was sent without an `Idempotency-Key` header, so nothing was started or charged. Distinct from `malformed_body` and `unsupported_parameter`, the other 400s: the body was never read.

What causes it

  • `POST /v1/workflows/{workflow_id}/runs` without the header. A board run starts every step of the board in one request, so a retry after a lost response must be recognisable as the same run.

What to do

Send `Idempotency-Key` with a unique value (at most 255 characters) per intended run, and resend the same value when you retry — the retry returns the run already started instead of starting a second one.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

unauthorized

HTTP 401Unauthorized

The credential did not authenticate. Every failure reason returns this one identical response.

What causes it

  • No `Authorization` header, on any route except the model list (`GET /v1/models` answers without one).
  • A malformed key.
  • A key that was never issued.
  • A revoked key — revocation takes effect immediately, on the next request.
  • An expired key.

What to do

Send `Authorization: Key ima_sk_live_…`. Mint and revoke keys at /developers/keys.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

The five causes are deliberately indistinguishable: telling you which one it was would turn this endpoint into an oracle for probing valid keys. A 403 rather than a 401 is the signal that your key itself is fine.

insufficient_credits

HTTP 402Insufficient credits

The account behind the key does not hold enough credits for this generation.

What causes it

  • Balance below the cost of the request at submit time.

What to do

Top up at the page in `top_up_url`, or call the pricing quote endpoint first and check the balance before submitting.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

Also carries credits_needed, credits_available and top_up_url.

forbidden_scope

HTTP 403Insufficient scope

The key is valid but was not issued with the scope this route requires.

What causes it

  • Calling `/v1/models/{model}` or the pricing quote with a key scoped only to `generate`. (The model list, `GET /v1/models`, needs no key and no scope.)
  • Calling a write route with a read-only key.

What to do

Issue a key carrying the scope the route needs. Scopes are fixed at creation; a key cannot be widened after the fact.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

A 403 here — as opposed to a 401 — is a useful diagnostic: it proves the credential itself authenticated.

model_not_found

HTTP 404Model not found

No model is published under that public id. One of three codes that return 404 — this one means the route was right and the model id was not.

What causes it

  • A typo in the model id.
  • An internal id used where a public id is required.

What to do

List the catalogue with `GET /v1/models` and use the `id` field verbatim.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

model_retired

HTTP 410Model retired

The model id was valid once and has been retired. The response names the successor in `extensions.successor`; nothing was queued and nothing was charged.

What causes it

  • An integration still sending a public id we have withdrawn in favour of a newer generation.

What to do

Switch to the `successor` id in the response. GET /v1/models lists everything currently published.

Do not retry.The identical request can never succeed.

Retirements are listed in shared-config/retired-models.ts with the date and the reason.

request_not_found

HTTP 404Request not found

No request exists with that id for this key’s owner. One of three codes that return 404; unlike `model_not_found` and `not_found`, the path and the model were both fine.

What causes it

  • An unknown request id.
  • A request id belonging to a different account.

What to do

Use the `request_id` returned by the submit call, with a key on the same account.

Do not retry.The identical request can never succeed.

Cross-account ids answer identically to unknown ones, on purpose.

not_found

HTTP 404Not found

No route is published at that path. Distinct from `model_not_found`, which means the route was right and the model id was not.

What causes it

  • A path outside the published surface.

What to do

Check the path against the API reference.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

method_not_allowed

HTTP 405Method not allowed

The route exists but not for that HTTP method.

What causes it

  • A `GET` where the route takes `POST`, or the reverse.

What to do

Read the `Allow` response header, which lists the methods the route accepts.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

request_cancelled

HTTP 409Request cancelled

The request was cancelled and will never produce a result.

What causes it

  • A prior successful call to the cancel endpoint.

What to do

Stop polling and submit a new request if you still need the output.

Do not retry.The identical request can never succeed.

Also carries deprecated_code.

Shares HTTP 409 with `idempotency_conflict`. The `code` is the branch point, not the status.

idempotency_conflict

HTTP 409Idempotency key in flight

You reused an `Idempotency-Key` whose original request is still running.

What causes it

  • A retry fired before the first attempt reached a terminal state.

What to do

Poll the original request rather than resubmitting. This is a “wait”, not a permanent failure — which is exactly what distinguishes it from `request_cancelled`, the other 409.

Retry with backoff.The identical request may succeed later.

409 rather than 202 because you have not been handed a `request_id` for this attempt, so answering “accepted” would be a lie.

project_limit_reached

HTTP 409Project limit reached

A board run needs a project to land in, and the account is at its plan’s project limit. Shares HTTP 409 with `idempotency_conflict` and `request_cancelled`; nothing was started or charged.

What causes it

  • The first API board run on an account that already keeps as many projects as its plan allows. Every API run lands in one project called “API runs”, created on first use.

What to do

Close a project you have finished with in Imagera, then retry. After the “API runs” project exists, later runs reuse it and do not count against the limit again.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

workflow_changed

HTTP 409Workflow changed

The board was saved between the price check and the start of a board run, so nothing was started or charged. Shares HTTP 409 with `project_limit_reached`, `idempotency_conflict` and `request_cancelled`.

What causes it

  • The board was edited in Canvas (or saved from another tab) while the run request was being priced.

What to do

Retry the same request. It is priced again against the board as it is now, and `max_credits` is checked against that price.

Retry with backoff.The identical request may succeed later.

limit_request_pending

HTTP 409Limit request pending

You asked for a higher parallel limit while an earlier request is still under review, so nothing new was filed. The body carries its `request_id`. Shares HTTP 409 with `project_limit_reached`, `workflow_changed`, `idempotency_conflict` and `request_cancelled`.

What causes it

  • `POST /v1/limits/requests` while a request filed from the API or from Settings is still pending (one at a time per account).

What to do

Check its status with `GET /v1/limits` (`latest_request`). Once it is decided you can ask again.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

invalid_request

HTTP 422Invalid request

The body parsed but failed validation against the model’s published input schema.

What causes it

  • A missing required parameter, such as `prompt`.
  • A parameter the schema does not declare — every model schema is `additionalProperties: false`.
  • A value of the wrong type, or outside the allowed range or enum.

What to do

Read `detail`, which names the offending parameters, and compare against `GET /v1/models/{model}`. Note that image inputs are `image_urls` — an array — on most models.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

Cancelling a request that has already left the pending state also answers with this code. In that one case `detail` is generic rather than naming a parameter: the request is already running and cannot be cancelled — poll it to completion instead. Board runs have three more 422 codes — `unpriced_steps`, `credit_cap_exceeded` and `workflow_not_runnable` — for refusals that are about the board or its price rather than your fields; on those routes `invalid_request` means the inputs themselves (`errors[].path` names them).

unpriced_steps

HTTP 422Unpriced steps

A board run was refused because at least one step has no firm price. One of four 422 codes; unlike `invalid_request`, your fields were valid.

What causes it

  • A step no model can run, or one the deployment could not price with these inputs.
  • A step priced from the measured length of a clip or track it reads, sent without `"allow_input_length_pricing": true`.

What to do

For steps that cannot be priced, fix them in Canvas or run to an earlier step with `target`. For length-priced steps, send `"allow_input_length_pricing": true` to accept `credits_quoted` as a minimum. `POST /v1/workflows/{id}/quote` shows which steps and why.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

Also carries unpriced_steps, input_length_steps and credits_quoted.

credit_cap_exceeded

HTTP 422Credit cap exceeded

The board’s quote for this run is above the `max_credits` you sent, so nothing was started. Shares HTTP 422 with `unpriced_steps`.

What causes it

  • `max_credits` lower than the priced steps of the board for these inputs.

What to do

Quote the run with `POST /v1/workflows/{id}/quote`, then raise `max_credits` or run less of the board with `target`. `credits_quoted` is in the response.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

Also carries credits_quoted and max_credits.

`max_credits` is checked against the quote before the run starts, and again as it opens. While it runs, a dispatch that would take it past `max_credits` is not bought: the run ends `failed` with a "Budget reached" error, keeping what it already made. A step whose model falls back to another during the run is billed at that model’s price.

workflow_not_runnable

HTTP 422Workflow not runnable

The board is unfinished — the Canvas lists issues on it — so it cannot run. Shares HTTP 422 with `invalid_request`: here the board needs editing, not your request.

What causes it

  • A step with a missing model, a broken wire, or a prompt that reads an input the board no longer has.

What to do

Open the board in Canvas, fix the listed `issues`, and retry. `GET /v1/workflows/{id}` shows them too.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

Also carries issues.

concurrency_limit_reached

HTTP 429Concurrency limit reached

The account already has as many generations in flight as its tier permits.

What causes it

  • Submitting while `max_parallel` generations are already running.

What to do

Wait for one to finish, then submit. This is a concurrency ceiling, not a request-rate ceiling — see `rate_limit_exceeded` for that.

Retry with backoff.The identical request may succeed later.

Also carries in_use, max_parallel, tier_key and upgrade_available.

rate_limit_exceeded

HTTP 429Rate limit exceededreturned by the edge, before the gateway

Too many requests from one credential or one address inside the window. Shares HTTP 429 with `concurrency_limit_reached`, which is a different ceiling: that one counts generations running at once, this one counts requests per minute.

What causes it

  • A runaway retry loop.
  • Bulk polling where a webhook would do.

What to do

Honour `Retry-After` and the `RateLimit` headers, which are exposed on every response. This ceiling is set far above any real integration; hitting it usually means a retry loop rather than genuine volume.

Retry with backoff.The identical request may succeed later.

Also carries limit, remaining, window_seconds and retry_after_seconds.

Emitted by the edge proxy in front of the gateway, so it is the one code that never originates from the generation path.

content_policy

HTTP 451Content policy

The input was refused by the content-safety gate.

What causes it

  • A prompt or an input image that the safety check declined.

What to do

Change the input. Retrying the same request unchanged will always fail.

Retry after changing the request.Fix the request first — resending it unchanged fails the same way.

internal_error

HTTP 500Internal error

Something unexpected failed on our side.

What causes it

  • A fault in the gateway or a dependency of it.

What to do

Retry with backoff. If it persists, contact support and quote the `request_id` from the response body.

Retry with backoff.The identical request may succeed later.

Also carries request_id.

Carries a `request_id` you can quote to support, and never a stack trace.

not_implemented

HTTP 501Not implemented

A capability this version does not serve yet.

What causes it

  • A route or option that is published but not live in v1.

What to do

Stated rather than failed silently, so you can branch on it. Check the reference for what v1 serves.

Do not retry.The identical request can never succeed.

provider_error

HTTP 502Generation service error

The generation service failed after the request was accepted.

What causes it

  • An upstream fault.
  • An input that passed our schema but was refused further down — model schemas do not publish every upstream constraint.

What to do

Retry with backoff. If it repeats for one specific input, treat the input as the cause and vary it.

Retry with backoff.The identical request may succeed later.

The message is fully sanitised, so it never names a supplier or an upstream model.

Still stuck?

Quote the code and, for a 500, the request_id from the response body — those two identify the request exactly.

Updated