Skip to content

Restore faces in a photo

restore_face repairs the faces in a scanned, blurred or heavily compressed photograph — the op you point at a box of family negatives or at a decade of low-resolution profile pictures. One call, one job handle, one price per image.

What it does

A model trained on faces finds each one and redraws it: eyes, skin and hair come back from a scan that had almost nothing left. The result is a new asset, so the original file — which is the archival copy — stays exactly as it was. The default model also enlarges in the same pass: parameters.scaleFactor is how many times the size it was handed the result comes back, 2 unless you set it — 1 keeps the size.

Reach for it for old prints, negatives, VHS frames and anything that has been through a chat app twice. Do not reach for it to make an already-sharp photo bigger — that is upscale — and do not reach for it to change a face: that is edit, with an instruction.

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.restoreFace 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.restoreFace("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 restore_face 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/restore_face — 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.

restore_face

ai job type ai-edit · input scaled to ≤ 1024 px on the long edge

Fix faces in old or low-quality photos. The default model also enlarges the result, by parameters.scaleFactor.

ParameterTypeWhat it does
modelstringA model whose categories include 'face_restoration' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed. default replicate/tencentarc/gfpgan
parametersobjectPassed to the model. The default model reads scaleFactor — how many times the size it was handed the repaired image comes back, 2 unless set, 1 to keep it — and version, the GFPGAN release to run. 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 replicate/tencentarc/gfpgan: $0.0028 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

An archive is a batch, and a batch wants two things: a fixed order of steps, and somewhere to look afterwards. Save the order as a preset so every photograph gets the same treatment, and put the outputs in a collection so you can find them.

{
  "name": "Archive restore",
  "slug": "archive-restore",
  "description": "Restore the faces, then colour the print.",
  "steps": [
    {
      "op": "restore_face"
    },
    {
      "op": "colorize"
    }
  ]
}
import { readFile } from "node:fs/promises";

const preset = await client.presets.create(JSON.parse(await readFile("archive-restore.json", "utf8")));
console.log(preset.slug, preset.version); // "archive-restore" 1
const price = await client.presets.run("archive-restore", ["<scan-id>","<another-scan-id>"], { dryRun: true }); // nothing is created
console.log(price.estimatedCredits, price.steps);

const job = await client.presets.run("archive-restore", ["<scan-id>","<another-scan-id>"], { wait: true });
const outputs = await client.jobs.outputs(job);

A box of prints is a long job: submit it without waiting and come back with the handle (waiting), and give it a collection so the restored prints are one search away.

Ingesting the scans is its own step: upload from disk, or hand the service a list of URLs and let it fetch them — both on the assets page. Either way, bytes you already hold come back as the asset you have (same SHA-1), so re-running an import does not double your library.

Limits

The default model is handed at most 1024 px on the long edge, so its result is at most that times scaleFactor — 2048 px at the default, whatever the scan's resolution. A scan larger than that comes back smaller than it went in: keep it, and restore a crop of the faces when you need more of them. Small, distant or heavily occluded faces are where the model has least to work from and invents most. A photo with no face in it is not an error — it comes back with nothing repaired (still resized by scaleFactor), and it is still an item you paid for, so filter first if you are running over a mixed library. Items that fail carry their own code and are not charged.

FAQ

Does it work on more than one face?
Yes — the model finds the faces in the image and repairs each. A crowd at the back of a wide shot is where results get uneven, because there is very little to work from.
Is the result the same person?
It is a plausible reconstruction, not a recovery. The model invents detail that was never in the file, so treat the output as a restoration, and keep the scan.
Should I restore first or upscale first?
Restore first, then upscale. restore_face is trained on faces and works from what the scan has; an upscaler run first enlarges the damage along with everything else.
What about the rest of the photo?
This op is about faces. For the background, follow it with upscale, or colorize if the photograph is black and white — save the pair as a preset and it stays one job.
What does it cost?
Credits per image at the model's published per-image price. The dry run prices the exact batch before anything is created or charged.