Skip to content

Upscale an image

upscale raises an image's resolution — 2× or 4× on the default model — and regenerates the detail while it does, instead of stretching the pixels it already has. One op of the catalogue, one job handle, one price the dry run will tell you in advance.

What it does

The model reads the image and writes a larger one, inventing texture that interpolation cannot: an edge stays an edge, a fabric keeps a weave. parameters.scaleFactor is how much bigger: 2 by default, or 4. The result is a new asset; the original is untouched.

Reach for it when a program is handed something too small to use — a marketplace thumbnail that has to become a hero image, an archive scan, a frame someone screenshotted. Do not reach for it to make a file merely larger on disk; that is resize, it is deterministic, and on a paid plan it is free. And upscaling does not undo compression artefacts: it enlarges them too.

Call it

One op, six surfaces, one vocabulary: the op name below is the same string everywhere — the SDKs' named helpers only spell it in their language's case, ops.upscale in JavaScript — because every surface enumerates GET /api/v1/ops instead of carrying its own list. The request is the catalogue's own example for this op, so it is the one the service has checked.

// pnpm add imagestep
const job = await client.ops.upscale("ast_1a2b", { parameters: { scaleFactor: 2 }, wait: true });
const [out] = await client.jobs.outputs(job);

The n8n tab is a workflow to paste onto the canvas: the upscale entry of the node's Op dropdown, which is filled from the same catalogue. Full references: SDKs, CLI, MCP, REST.

Parameters, model and price

Read live from GET https://api.imagestep.dev/api/v1/ops/upscale — the same entry the SDKs, the MCP server and the n8n node enumerate, so this table cannot fall behind the API. The rows are fields of the request body. parameters goes to the model: its keys are the model's own, listed under parameters for each model in GET /api/v1/ai-models, and a key the model does not declare, or a value outside its options or bounds, is 400 invalid_param naming it.

upscale

ai job type ai-edit · typically ~15 s per item · input scaled to ≤ 1024 px on the long edge

Increase resolution by parameters.scaleFactor, regenerating detail rather than interpolating it. The model is handed at most maxInputEdge px on the long edge, so that times the factor is the largest result.

ParameterTypeWhat it does
modelstringA model whose categories include 'upscale' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed. default fal-ai/clarity-upscaler
parametersobjectPassed to the model. The default model reads scaleFactor, 2 (the default) or 4 — any other value is 400 invalid_param — and resemblance, how closely the result keeps to the input. Another model's are its parameters in GET /api/v1/ai-models; a key or value a model does not declare is 400 invalid_param.

AI op: per-item USD from the model's price; credits are charged per item. Price a batch with POST /api/v1/jobs?dryRun=true before spending. Default model fal-ai/clarity-upscaler: $0.1512 - $0.6048 per item.

That is how it is priced. What one request costs — another model, a bigger scale factor, forty images — is the dry run, which prices the exact body you are about to send without creating anything.

In a workflow

Upscaling is the expensive step, so the two patterns worth knowing are both about spending it deliberately: price the batch before you submit it, and re-encode afterwards so the extra pixels do not become an extra megabyte on every page.

// Price it first — nothing is created, nothing is charged.
const quote = await client.ops.upscale(assetIds, { parameters: { scaleFactor: 4 }, dryRun: true });
if (!quote.sufficientCredit) throw new Error(`needs ${quote.estimatedCredits}, have ${quote.creditBalance}`);

// Then run it, and hand the output to a deterministic step — free on a paid plan.
const job = await client.ops.upscale(assetIds, { parameters: { scaleFactor: 4 }, wait: true });
const outputs = await client.jobs.outputs(job);
await client.ops.convert(outputs.map((a) => a.id), { format: "webp", quality: 82 }, { wait: true });

Over REST these are the three requests of jobs, one after the other; an agent makes them one tool call at a time — transform with dry_run, then without.

A long batch is where webhooks earn their keep: submit, return, and let job.completed wake the rest of your flow. From an agent, the MCP server hands back the job handle when a wait runs out, so the conversation keeps its place instead of losing the work.

Limits

The default model is handed at most 1024 px on the long edge — a larger input is scaled down first — so its largest result is 2048 px at 2× and 4096 px at 4×. An input already past that edge comes back less than the factor larger, and running upscale on an output gains nothing. It takes scaleFactor 2 or 4 and nothing between: any other value is 400 invalid_param on parameters.scaleFactor, before anything is spent. 4× is the slowest thing this API does; expect a job you wait on, not a call you block a request handler with. Text and faces are where upscalers hallucinate most; a sign in the background may come back saying something else. Items that fail inside a job carry their own code and retryable flag and are not charged.

FAQ

How is this different from resizing up?
resize interpolates: a 2× enlargement has the same detail spread over four times the pixels, so it looks soft. upscale runs a model that invents plausible detail, which is why it costs credits and resize does not.
How big can it go?
The default model enlarges 2× or 4× (scaleFactor; any other value is 400 invalid_param), from an input it is handed at most the catalogue's maxInputEdge on the long edge — so that edge times the factor is the largest result, whatever the size you started with. Upscaling the output again gains nothing, because the second run's input is scaled back down first. For more, pick an upscaler that goes further (its factors are under parameters in GET /api/v1/ai-models), and check the price: several providers bill by output megapixel, so 4× is not twice the price of 2×.
Why is the price a range?
Because the provider's is. The dry run prices the exact scale factor and model you are about to send, so the number you see is the number you are charged.
Can it fix a face?
Not reliably — an upscaler enlarges what is there. For portraits use restore_face, which is trained on faces, and upscale afterwards if you still need the pixels.
Can I upscale a whole collection in one call?
Yes. List the collection, then one job takes every asset id in assetIds; items settle one by one, a failed item is refunded on its own, and job.completed fires once at the end.