Skip to main content

Upscaling images

An upscale re-renders one of your images at a larger size, 2K or 4K, with an AI model. The result is a new asset; the original is never changed. It is a faithful re-render, not a pixel-exact resample, so the output can differ in fine detail. Keep it or delete it — a delivered render is charged either way.

Like a run, an upscale spends credits and is asynchronous: POST /upscales (createImageUpscale) answers 202 at once, and you poll GET /upscales/{upscaleId} (getImageUpscale) until it is final.

Permissions​

Creating needs a key with the runs:write scope and a plan that includes the Create API; reading needs runs:read. These are the same scopes and rate limits as runs. A size your plan does not include is refused with 403 IMAGE_UPSCALE_NOT_ENTITLED before anything is read or charged.

1. Create an upscale​

Idempotency-Key is required (see Idempotency). Name the image by an asset you own, or by one of a product's own images; a URL is never accepted.

curl -X POST https://api.mocart.io/api/public/v1/upscales \
-H "Authorization: Bearer $API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{ "source": { "assetId": "asset_123" }, "target": "2K" }'
FieldMeaning
source{ "assetId": "…" } (a still image of yours, from any source) or { "productId": "…", "image": "primary" }; image may also be gallery, with an optional index (default 0).
target2K or 4K. It must be larger than the source: a source whose long edge is already 2048 px (2K) or 4096 px (4K) is refused.

The 202 carries the upscale and a Location header:

{
"object": "image_upscale",
"upscaleId": "3f6c…",
"status": "QUEUED",
"source": { "assetId": "asset_123" },
"target": "2K",
"assetId": "3f6c…",
"output": null,
"error": null,
"createdAt": "2026-10-04T10:00:00.000Z",
"completedAt": null
}

A retry with the same key and body returns the same upscale and is never charged twice. The same key with a different body answers 409 IDEMPOTENCY_KEY_REUSED.

2. Poll​

status is QUEUED, PROCESSING, SUCCEEDED or FAILED; the last two are final. Poll every few seconds.

On SUCCEEDED, the new image is the asset assetId (always equal to upscaleId): read it with GET /assets/{assetId}. output carries its pixel size once the asset has been measured, and can be null for a short while after success. That asset carries a derivation:

"derivation": { "kind": "ai_rerender", "operation": "image_upscale", "parentAssetId": "asset_123", "rootAssetId": "asset_123" }

derivation appears only on an asset made this way. parentAssetId and rootAssetId are null when the source was a product image rather than an asset.

On FAILED, error.code says why, and nothing is charged:

error.codeMeaningWhat to do
SAFETY_REFUSEDThe content policy refused the image.Do not retry the same image.
TRY_AGAINA temporary failure.Send the request again (new key).
RENDER_FAILEDThe render could not be completed.Retrying is unlikely to help.
CANCELLEDThe upscale was cancelled.—

Refusals​

Decided before anything is held, so the Idempotency-Key is not used up:

StatusCodeMeaning
402INSUFFICIENT_CREDITSThe account cannot cover the upscale.
403IMAGE_UPSCALE_NOT_ENTITLEDThe plan does not include this size.
404ASSET_NOT_FOUND / PRODUCT_NOT_FOUNDNot yours, deleted, not a still image, or an empty slot — indistinguishable.
409IMAGE_UPSCALE_IN_PROGRESSThis image is already being upscaled to this size.
409IMAGE_UPSCALE_UNPRICEDThe size has no published price yet.
422IMAGE_UPSCALE_TARGET_NOT_LARGERThe image is already as large as the target; detail states its size.
422IMAGE_UPSCALE_SOURCE_UNREADABLEThe image could not be read.
422VALIDATION_ERRORA malformed body, an unknown field, or target other than 2K / 4K.

Another account's upscaleId reads 404 IMAGE_UPSCALE_NOT_FOUND, the same answer as an unknown one.