• Docs
  • API reference
  • SDKs & tools
  • Models
  • Changelog
Sign inGet an API key
DocsAPI referenceSDKs & toolsModelsChangelog

Basics

IntroductionAuthenticationThe task objectWebhook payloadsErrors

Endpoints

POSTGenerate imagePOSTEstimate costPOSTUpload imagesPOSTGenerate alt textPOSTReview imagePOSTRate imagePOSTRemove backgroundPOSTUpscale image
GETList tasksGETGet taskGETGet task statusGETGet task result
  1. API reference
  2. /
  3. Basics

Errors

The shapes an error takes, every request error code and task failure code, how a failed task differs from a rejected request, and what is safe to retry.

PreviousWebhook payloadsNextGenerate image

On this page

Request errorsValidation errorsMalformed requestsTask failuresCodesWhat is safe to retryIn the SDK and CLINext steps

Two different things fail, and they look nothing alike. A request error rejects the call before anything is queued, with a 4xx or 5xx right away. A task failure happens after a 201: the task runs and the work fails seconds or minutes later.

POST /image/generate
  │
  ├─ rejected   4xx / 5xx   { "error": { "code": "unauthorized", "message": "..." } }
  │                         nothing queued, held, or charged
  │
  └─ 201 { task_id } ──▶ task runs ──┬─▶ completed   items can still carry an error
                                     └─▶ failed      errors: [{ code, message? }]

Handle both.

Request errors

Every request error has the same body: an error object with a code in lowercase snake_case and a message that says what to change:

{
  "error": {
    "code": "insufficient_balance",
    "message": "This request needs $0.12, which is more than your available balance. Top up at https://mynth.io/dashboard/wallet, or lower the cost (fewer images, a cheaper model). Nothing was queued or charged."
  }
}

Every error has this envelope, including a 404 for an unknown path, a 415, and a 500. No error response has data, and no success response has error.

Branch on code, never on message. The message is written for people and may change. It is optional: invalid_token and token_expired have none. validation_error can add an issues array, insufficient_scope and scope_escalation add a scopes object, and public_access_token_failed adds task_id, all inside error.

StatusCodeMeansDo
400validation_errorThe body, query, or path failed validation. See belowFix what message names
400invalid_jsonThe body is not valid JSON. message includes the parser's positionFix the JSON
400api_key_limit_reachedThe account already has 100 live keysDelete a key first
400webhook_api_key_not_foundapi_key_ids names a key you don't haveDrop the unknown id
400destination_invalid_providerA destination update changed provider.idCreate a new destination
401unauthorizedNo usable bearer header, or an unknown or deleted key. message says whichFix the credential
401invalid_tokenA pat_ that is malformed or not signed by MynthGet a new token from your server
401token_expiredA pat_ past its hourPoll from the server, or use a webhook
402insufficient_balanceAvailable balance is below the task's estimate. message states the estimateTop up. Nothing was queued
402spending_limit_exceededThe key's cap for the current day, week, or month is used up. message states the cap, what is used, and what the request needsWait for the period, or raise the cap
403insufficient_scopeValid key, missing scope. Adds scopes.required and scopes.currentUse a key with the scope, or add it
403scope_escalationAn API key asked for manage or keys. Adds scopes.requested, scopes.allowedDo it in the dashboard
404task_not_foundUnknown task, another account's task, or a pat_ for a different taskCheck the id and the credential
404api_key_not_foundUnknown key idCheck the id
404destination_not_foundUnknown destination idCheck the id
404webhook_not_foundUnknown webhook idCheck the id
404not_foundNo route matches the method and path. message names bothCheck the URL
409destination_name_takenYou already have a destination with that namePick another name
413payload_too_largePOST /image/upload body over 100 MBUpload fewer or smaller files
415unsupported_media_typeA JSON endpoint without Content-Type: application/json, or an upload without multipart/form-dataSet the Content-Type header
500internal_server_errorOur fault, including a failure to store or read a destination secretRetry, after checking GET /tasks
500public_access_token_failedThe task was created and will run, but its pat_ could not be generated. Adds task_idFollow task_id. Don't resend
502destination_test_failedYour storage rejected the test upload. message is the provider'sFix the credentials or path

A 429 means back off and retry. There is no Retry-After header. Rate limits has the rest.

Validation errors

validation_error covers two kinds of check, with one shape.

A field that fails the schema: a missing prompt, a count outside 1 to 20 or not a whole number, an unknown model id, a size that is not a size, or a field the endpoint does not define. These add issues, one entry per invalid field, and message states all of them:

{
  "error": {
    "code": "validation_error",
    "message": "model: \"flux.2-pro\" is not supported. Did you mean \"black-forest-labs/flux.2-pro\"? See https://mynth.io/models, or GET /models, for the model IDs. count: Too big: expected number to be <=20.",
    "issues": [
      {
        "path": ["model"],
        "message": "\"flux.2-pro\" is not supported. Did you mean \"black-forest-labs/flux.2-pro\"? See https://mynth.io/models, or GET /models, for the model IDs."
      },
      { "path": ["count"], "message": "Too big: expected number to be <=20" }
    ]
  }
}

path points into the body, query, or path parameters: ["inputs", 0, "source"] is inputs[0].source, and an empty path means the body as a whole. Map issues onto your own form fields, or show message as it is. The response does not echo your request.

Each issue in message starts with its field path, such as count: or inputs[0].source:. An issue about the whole body has no path prefix. Use code and issues[].path to handle errors in code, since the wording of message can change.

A request that fits the schema but not the model or your account has no issues, only message:

  • a size the pinned model cannot produce, such as 16:9_4k on a model without 4k. The message names the size to use instead
  • inputs the pinned model cannot take, or too many of them
  • a video resolution tier, duration, or audio the model does not offer. The message lists the values it does
  • metadata over 2048 bytes
  • a destination name you don't own
  • an upload with no files, more than 10 files, a file that is not JPEG, PNG, or WEBP, or a file under 1 KB or over 10 MB. The message names the file

With model: "auto", input and size checks wait until the worker picks a model; see task failures.

Every request body rejects fields it does not define, at any depth. A typo such as {"prompt": "...", "widht": 512} answers 400 with an issue at ["widht"] instead of running without it. A camelCase name, such as aspectRatio, gets a message saying field names are snake_case.

Malformed requests

Before the body is validated, it has to be JSON, sent as JSON:

{
  "error": {
    "code": "unsupported_media_type",
    "message": "The request has no Content-Type header. Send the body as application/json and set Content-Type: application/json."
  }
}
{
  "error": {
    "code": "invalid_json",
    "message": "The request body is not valid JSON (Expected double-quoted property name in JSON at position 17 (line 1 column 18)). Check for trailing commas, unquoted keys, or a truncated body."
  }
}

POST /image/upload is the exception: it takes multipart/form-data, and a JSON body there answers 415 unsupported_media_type.

Task failures

An accepted task settles as completed or failed, and both can carry error codes. Where the code lands depends on the task type:

Task typeA failure appears in
image.generateresult.images[].error, task stays completed
video.generateresult.videos[].error, task stays completed
image.remove_background, image.upscale, image.rate, image.alt, image.reviewerrors[], task is failed

So a refused prompt on POST /image/generate gives you 201 at creation and a completed task whose only image failed. Checking status === "completed" is not enough. Walk the array.

{
  "status": "completed",
  "result": {
    "model": "black-forest-labs/flux.2-pro",
    "images": [
      {
        "status": "failed",
        "error": {
          "code": "restricted_content",
          "message": "The request was blocked by content moderation."
        }
      }
    ]
  }
}

An image.generate or video.generate task reaches failed only when the work around generation broke: resolving the model, the inputs, or the size, queueing the task, or the daily sweep that expires a task stuck on pending. With a pinned model, inputs and sizes it cannot serve are rejected at create with 400 validation_error. With model: "auto" they are only known once a model is picked, so a _4k size or an edit that lands on a model without support fails the task with capability_not_supported.

Codes

CodeMeansRetry helps
restricted_contentThe provider or the model refused the content. Mynth did notMynth does not retry. You may resend
invalid_promptThe prompt was empty or too long for the modelNo
invalid_inputAn input image could not be fetched or readNo. Check the URL is publicly reachable
capability_not_supportedThe model does not support an option you sentNo. Use another model or option
output_too_largeimage.upscale would produce more than 4096x4096 pixelsNo. Send a smaller image or factor
providers_busyNo provider for that model has capacity right nowYes, later
rate_limitedProviders kept rate-limiting the requestYes, shortly
timeoutThe provider did not answer in timeYes
provider_errorThe provider failed for another reasonYes
fetch_failedimage.rate, image.alt, or image.review could not fetch urlOnly if the URL was flaky
rating_failedimage.rate ran but produced nothing usableYes
alt_generation_failedimage.alt ran but produced nothing usableYes
review_failedimage.review ran but produced nothing usableYes
task_expiredStill pending 24 hours after its last update, then sweptYes. Something broke on our side
enqueue_failedQueueing failed right after creation. The create answered 500Yes
unknown_errorNothing else matchedOnce

Before a retryable code reaches you, Mynth has already retried across providers: up to four attempts per image and three per video. See tasks. What restricted_content means is on prompts and images.

Webhooks carry the same values: .completed events carry result, and .failed events carry errors.

What is safe to retry

  • 400, 401, 403, 404, 409, 413, and 415 fail the same way every time. Fix the request.
  • 402 insufficient_balance clears when you top up. 402 spending_limit_exceeded clears when the key's period rolls over or you raise the cap.
  • 502 destination_test_failed is your storage rejecting the upload. Fix the destination.
  • 500 and connection failures are worth retrying, with one catch: there are no idempotency keys. Every create mints a new task and holds cost again, so resending a create that actually succeeded runs and charges it twice. If a create times out, check GET /tasks before sending it again.
  • A create that fails while queueing marks its task failed with enqueue_failed, releases the hold, and answers 500. Retrying that one is safe.
  • 500 public_access_token_failed is the opposite: the task was created and will run. Follow error.task_id with your API key instead of resending.
  • A settled task never runs again. Retrying a task failure means a new request. Failed items are not charged.

In the SDK and CLI

The SDK throws MynthAPIError for a request error, with status, code, message, and issues, and TaskAsync* errors while waiting on a task. SDK errors covers both.

The CLI prints the message to stderr and exits with a code scripts can branch on: 2 usage, validation_error, invalid_json, and unsupported_media_type, 3 auth, 4 balance or spending limit, 5 restricted_content, 6 a 429, 1 anything else. CLI commands has the table.

Next steps

  • Tasks: statuses, partial failure, and attempts.
  • Authentication: the auth codes in context.
  • Webhooks: the same codes, pushed to you.