Colorize a black-and-white photo
colorize gives a monochrome photograph colour. It is the simplest op in the catalogue to call — an image in, nothing to tune — and like every AI op it runs as a job with a price you can ask for first.
What it does
The model reads the scene — skin, sky, foliage, fabric — and writes a colour version of it as a new asset. Nothing you send names a colour: the decisions are which image and which model, and the catalogue names the default. The monochrome original is untouched.
Reach for it for archives, scanned prints and film frames, and for anything a person will look at rather than measure. Do not reach for it when you need a particular colour — that is edit with an instruction. Going the other way is free: grayscale is deterministic, costs no credits on a paid plan, and has a synchronous form.
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.colorize 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.
JavaScript
// pnpm add imagestep
const job = await client.ops.colorize("ast_1a2b", { wait: true });
const [out] = await client.jobs.outputs(job);Python
# pip install imagestep
job = client.ops.colorize("ast_1a2b", wait=True)
[out] = client.jobs.outputs(job)CLI
imagestep jobs submit --op colorize --asset-ids ast_1a2b --waitcurl
# "wait" holds the response until the job is done (60 s at most): 200 with the finished job,
# or 202 with the handle — then GET /api/v1/jobs/<id>?wait=30 waits again
curl -sS -X POST https://api.imagestep.dev/api/v1/jobs \
-H "Authorization: ApiKey $IMAGESTEP_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"op":"colorize","assetIds":["ast_1a2b"],"wait":30}'MCP
# tool call
transform {"op":"colorize","asset_ids":["ast_1a2b"]}n8n
{
"nodes": [
{
"parameters": {
"resource": "op",
"operation": "run",
"op": "colorize",
"inputMode": "assetIds",
"assetIds": "ast_1a2b"
},
"name": "ImageStep",
"type": "n8n-nodes-imagestep.imageStep",
"typeVersion": 1,
"position": [
0,
0
]
}
],
"connections": {}
}The n8n tab is a workflow to paste onto the canvas: the colorize 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/colorize — 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.
colorize
ai job type ai-edit · input scaled to ≤ 2048 px on the long edgeColour a black-and-white photo.
| Parameter | Type | What it does |
|---|---|---|
| model | string | A model whose categories include 'colorize' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed. default replicate/piddnad/ddcolor |
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/piddnad/ddcolor: $0.0013 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
Colourising is usually the last AI step of an archive pipeline and the first thing a person wants to compare against the original. Keep both, label them together, and let a webhook tell your flow when the batch is done.
{
"name": "Archive colour",
"slug": "archive-colour",
"description": "Faces first, then colour — the scans untouched.",
"steps": [
{
"op": "restore_face"
},
{
"op": "colorize"
}
]
}JavaScript
import { readFile } from "node:fs/promises";
const preset = await client.presets.create(JSON.parse(await readFile("archive-colour.json", "utf8")));
console.log(preset.slug, preset.version); // "archive-colour" 1Python
import json
preset = client.presets.create(json.load(open("archive-colour.json")))
print(preset["slug"], preset["version"]) # "archive-colour" 1CLI
imagestep preset create -f archive-colour.json -o jsoncurl
curl -s https://api.imagestep.dev/api/v1/presets -H "Authorization: ApiKey $IMAGESTEP_API_KEY" -H "Content-Type: application/json" -d @archive-colour.jsonMCP
# tool call — an agent has no disk to read, so the document travels in the call
save_preset {"name":"Archive colour","slug":"archive-colour","description":"Faces first, then colour — the scans untouched.","steps":[{"op":"restore_face"},{"op":"colorize"}]}JavaScript
const job = await client.presets.run("archive-colour", ["<scan-id>"], { wait: true });
const outputs = await client.jobs.outputs(job);Python
job = client.presets.run("archive-colour", ["<scan-id>"], wait=True)
outputs = client.jobs.outputs(job)CLI
imagestep jobs submit --preset-id archive-colour --asset-ids <scan-id> --wait -o jsoncurl
# price it first with ?dryRun=true; "wait" holds the response until a one-item job is done (60 s at most)
curl -s https://api.imagestep.dev/api/v1/jobs -H "Authorization: ApiKey $IMAGESTEP_API_KEY" -H "Content-Type: application/json" \
-d '{"presetId":"archive-colour","assetIds":["<scan-id>"],"wait":30}'MCP
# tool call
run_preset {"preset":"archive-colour","asset_ids":["<scan-id>"]}No polling on a batch: register a webhook once and let job.completed continue the flow when it lands.
In n8n the same two steps are the ImageStep node twice, or once with the preset; the ImageStep Trigger is the webhook end of it. To show a before and after, publish both assets — the original keeps its own id and URL.
Limits
The default model is handed at most 2048 px on the long edge, so that is the largest result: a scan above it comes back scaled down to it, the original kept as it was. Expect plausible, not faithful: the model has no way to know that the dress was blue. Heavy grain, low contrast and damage push it toward washed-out results, which is why restore_face usually goes first. Nothing here names a colour, so when a result is wrong the lever is edit, with words.
FAQ
- Can I tell it what colour something is?
- Not through colorize — it takes no prompt and nothing that names a colour. When the colour matters, use edit with an instruction ("the coat is dark green"), which is a prompt-driven op.
- Are the colours historically accurate?
- No. The model infers plausible colour from what the scene looks like; it has no knowledge of the particular coat, car or uniform. Label the output as colourised.
- What happens if I colorize a colour photo?
- Nothing useful. Run it on monochrome input; a colour image comes back changed for no reason and still costs an item.
- Restore the faces first, or colorize first?
- Restore first. restore_face works from the detail the scan still has, and colouring beforehand gives it invented colour to repair.
- Is there a free way to do the opposite?
- Yes — grayscale is a deterministic op: no credits on a paid plan, exact every time, and it runs synchronously if you only want the bytes back.