Upscale an image (asynchronous)
Sample request
curl -X POST \
-H "x-api-key: $ABYSSALE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"image_url": "https://example.com/product.jpg",
"upscale_factor": 2,
"callback_url": "https://example.com/webhooks/abyssale"
}' \
https://api.abyssale.com/images/upscale
# {"job_id":"8f0c2a4e-…","status":"PENDING"} — then poll it, or wait for the webhook.Example request body
{
"image_url": "https://example.com/product.jpg",
"upscale_factor": 2,
"callback_url": "https://example.com/webhooks/abyssale"
}Example 202 response
{
"job_id": "8f0c2a4e-4869-11f0-96f2-0a00d9eb8f78",
"status": "PENDING",
"version": "v2026-11-09"
}Example 400 error response
{
"id": "unknown_enum_value",
"message": "upscale_factor '3' is not supported by the model 'bria-increase-resolution'.",
"errors": [
{
"path": "upscale_factor",
"code": "unknown_enum_value",
"message": "Must be one of: 2, 4.",
"expected": [
"2",
"4"
]
}
]
}Upscale one image by a factor (its width and height are each multiplied by upscale_factor), with an AI model. The work runs in the background: this call answers 202 with a job_id in milliseconds, before anything is processed. Follow the job by polling GET /asset-jobs/{jobId}, or pass a callback_url to receive the ASSET_JOB_FINALIZED webhook when it ends. The finished job has exactly one entry in outputs, a WebP image (image/webp).
Behaviour, limits and caveats
Spends AI credits, not generation credits, and only once the job completes. The price depends on the output size — width × height × upscale_factor², in megapixels, for most models — so it is known only once the image has been read: it is on the finished job (credits.ai_credits). Before anything is queued, only a non-empty AI credit balance is checked (402 insufficient_credits otherwise); the exact price is checked when the job runs, and a balance that does not cover it ends the job FAILED with insufficient_credits, without charging anything.
Image size. The image must be at least 128 × 128 px; a larger one than 3 840 px on its longest side is scaled down to it first. Each model caps the size of the result; an image too small, or a result over the model's cap, ends the job FAILED with invalid_input_asset and a message saying why — use a smaller factor or a smaller image.
Models. Omit model to use the workspace's default for upscale (set in the workspace settings, otherwise Abyssale's default). A model disabled in the workspace settings answers 400 ai_model_disabled.
upscale_factor depends on the model. GET /ai-models?operation=images_upscale lists, per model, the accepted factors and the default. Omit it to get the model's default. A factor the model does not accept answers 400 unknown_enum_value before anything is queued, with the accepted values in errors[].expected.
At most a few jobs per workspace run at once (pending or processing, every kind of asset job together). Past that, a new one answers 429 too_many_asset_jobs: wait for one to finish, then retry. This endpoint has no per-minute budget of its own — that cap is what bounds it.
Never retried for you, and do not retry blindly. Every 202 is a new job, charged on its own. A 503 (asset_jobs_disabled, ai_service_unavailable) means no job was created and nothing was charged.
Request body
image_url string (uri) required
Public http(s) URL of the image: PNG, JPEG or WebP, up to 20 MB. It is downloaded when the job runs. At most 2048 characters.
model string
The AI model to use, by id — one listed by GET /ai-models?operation=images_upscale. Omitted, the workspace's default is used. An id that is not an upscale model answers 400.
upscale_factor number | string
How many times larger the result is, on each side — one of the model's upscale_factors.allowed in GET /ai-models?operation=images_upscale, as a number (2) or as listed ("2"). Omitted, the model's default. Another value answers 400 unknown_enum_value with the accepted ones in errors[].expected.
callback_url string (uri)
The http(s) URL called once the job ends, completed or failed. Length 5–2048 characters.
More about callback_url
The http(s) URL called once the job ends, completed or failed.
We will do a POST request of the ASSET_JOB_FINALIZED event on this URL with a JSON payload.
Response 202
The job is queued; poll job_id or wait for the webhook.
job_id string (uuid) always
Identifier of the job — poll GET /asset-jobs/{jobId} with it.
status string always
Always PENDING here. One of "PENDING".
version string
The API version that produced this response, named by release date (vYYYY-MM-DD). Pattern ^v\d{4}-\d{2}-\d{2}$.
More about version
The API version that produced this response, named by release date (vYYYY-MM-DD). Stamped as a top-level field on JSON object bodies, success and error alike, so a client can always tell which contract answered. There is no version-selection parameter — a single version is maintained at a time.
Two kinds of body are not stamped. Array bodies (the listings) carry no envelope. And a body that already has a version key of its own is left alone — which in practice means Banner, whose version is the generated file's integer counter. So GET /banners/{bannerId} and the synchronous generate are the two responses that do not tell you which contract answered.
The value changes when a new version is released. Match the vYYYY-MM-DD shape rather than pinning today's literal, or your client breaks on the next release.
Errors
Every failure uses the shared error envelope — {id, message, errors?}. Branch on id, never on message. See Errors.
| Status | When |
|---|---|
400 | Bad Request — nothing was queued. Besides an invalid body (invalid_payload), unknown_enum_value when upscale_factor is not one the model accepts; errors[].expected lists the accepted values. ai_model_disabled when the model, or every upscale model, is disabled in the workspace settings. |
401 | Unauthorized — missing or invalid API key. |
402 | The workspace has no AI credits left (insufficient_credits). Nothing was queued. |
429 | Too Many Requests. |
500 | Internal Server Error — an unexpected error occurred on our side. |
503 | The job could not be started right now — no job was created and nothing was charged. asset_jobs_disabled while the feature is switched off, ai_service_unavailable when a service it depends on cannot be reached. Retry later. |
