Skip to content

Ops & models

An op does one thing to an image — remove the background, resize, convert — with a closed, validated set of parameters and a price per item. The catalogue below is the one vocabulary: op on POST /api/v1/jobs, the MCP transform tool, the n8n node's Op dropdown, the CLI's jobs submit --op (and its image subcommands for the ops that run synchronously) and the SDKs' ops.run all take a name from it, so an op added here works everywhere the day it ships. It answers without a key.

const ops = await client.ops.list(); // [{ op, kind, params, defaultModel, syncEndpoint, example, … }]

Three kinds

kindWhat it isRuns asCosts
aiA model does the work: generate, edit, remove_bg, upscale, restore_face, colorize, analyze. Each has a default model you can override (Models, below).always a job — a provider call needs an owner for its retry and refund. With wait, a one-image job still answers in the same HTTP callcredits per item, at the model's per-image USD price
deterministicNo model, the same output for the same input every time: resize, convert, compress, crop, pad, grayscale, rotate, flip, flop, trim, overlay, caption, mask, blur_region, adjust, flatten, render_template. render_template draws an HTML template in a headless browser, one PNG per row of data; the rest are pixel work on Sharp, starting from an image.a job when you want the result kept as an asset; synchronously (bytes in, bytes out, nothing stored) when you only want it back — every one but overlay, which reads a stored layer and runs as a job onlyfree on paid plans; on Free, counted against the monthly allowance and paid from the balance past it (the catalogue's pricing.overageCredits)
syncReads, not transforms: read_metadata. Answered from the asset record or from the bytes you send.never a job; GET /api/v1/assets/{id} or POST /api/v1/images/metadatafree

The catalogue says how an op is priced. The exact price of one request — another model, an upscale's scale factor, a batch of forty — is the dry run, which prices the body you are about to send without creating anything.

The catalogue

Read live from GET https://api.imagestep.dev/api/v1/ops, one card per op. Six of them have a page of their own — Remove background · Upscale · Generate · Edit with a prompt · Restore faces · Colorize — with when to reach for it, a workflow around it and the same call in six surfaces. Each card ends with the catalogue's example: the body POST /api/v1/jobs takes, which every SDK, the CLI, the MCP server and the n8n node post as it is.

Every deterministic op but render_template also takes four shared parameters, at the end of its table: metadata (strip, the default, removes EXIF, ICC and XMP — a phone's GPS position included; keep carries them over), frame (the first frame of an animation, or all), colorSpace (srgb) and density (the DPI an SVG is drawn at).

generate · edit · remove_bg · upscale · restore_face · colorize · analyze · resize · convert · compress · crop · pad · grayscale · rotate · flip · flop · trim · overlay · caption · mask · blur_region · adjust · flatten · render_template · read_metadata

generate

ai job type ai-generate · typically ~10 s per item · input scaled to ≤ 1536 px on the long edge

Text → image. Returns one asset per generated image.

ParameterTypeWhat it does
promptstringWhat to draw.
countintegerImages to generate (1–10). default 1
modelstringA model whose categories include 'image_generate' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed. default google/gemini-3.1-flash-image-preview
parametersobjectPassed to the model; GET /api/v1/ai-models lists each model's own, and a key or value it does not declare is 400 invalid_param. The default model reads aspectRatio and imageSize — the output size, whatever the input's, and what its price depends on.

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 google/gemini-3.1-flash-image-preview: $0.0567 - $0.1903 per item.

The request — the catalogue's example
{
  "op": "generate",
  "prompt": "a red bicycle on a white background",
  "count": 1
}

generate in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

edit

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

Image + prompt → image (instruction edit, style transfer, inpaint by description).

ParameterTypeWhat it does
promptstringThe edit instruction.
modelstringA model whose categories include 'image_edit' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed. default google/gemini-3.1-flash-image-preview
parametersobjectPassed to the model; GET /api/v1/ai-models lists each model's own, and a key or value it does not declare is 400 invalid_param. The default model reads aspectRatio and imageSize — the output size, whatever the input's, and what its price depends on.

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 google/gemini-3.1-flash-image-preview: $0.0567 - $0.1903 per item.

The request — the catalogue's example
{
  "op": "edit",
  "assetIds": [
    "ast_1a2b"
  ],
  "prompt": "replace the background with a plain warm-grey studio wall"
}

edit in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

remove_bg

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

Cut the subject out; transparent PNG result.

ParameterTypeWhat it does
modelstringA model whose categories include 'background_removal' — 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/bria/background/remove

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/bria/background/remove: $0.0227 per item.

The request — the catalogue's example
{
  "op": "remove_bg",
  "assetIds": [
    "ast_1a2b"
  ]
}

remove_bg in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

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.

The request — the catalogue's example
{
  "op": "upscale",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "scaleFactor": 2
  }
}

upscale in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

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.

The request — the catalogue's example
{
  "op": "restore_face",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "scaleFactor": 2
  }
}

restore_face in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

colorize

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

Colour a black-and-white photo.

ParameterTypeWhat it does
modelstringA 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.

The request — the catalogue's example
{
  "op": "colorize",
  "assetIds": [
    "ast_1a2b"
  ]
}

colorize in depth — when to reach for it, what a workflow around it looks like, and the same call in six surfaces.

analyze

ai job type parse · input scaled to ≤ 1024 px on the long edge · answers with JSON, not an image

Image → structured JSON: a description, search tags, objects, the scene and any legible text, or the shape you pass as parameters.schema. Each item's answer is its output (items[].output on GET /api/v1/jobs/{id} and on the job.item.completed webhook). With the default schema, the answer's tags and objects are also added to the asset's tags, so GET /api/v1/assets?tag= finds it; a schema of your own leaves the asset alone. Produces no new asset.

ParameterTypeWhat it does
promptstringWhat to look for (≤ 2000 bytes). Default: describe the image and tag it for search.
modelstringAny model from GET /api/v1/ai-models?mode=analyze. default google/gemini-2.5-flash-lite
parametersobject{schema: a JSON Schema object the answer must match (≤ 6000 bytes), maxOutputTokens: 256–4096, default 1024}. The price is per image at maxOutputTokens, whatever the prompt and schema say.

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 google/gemini-2.5-flash-lite: $0.0013 per item.

The request — the catalogue's example
{
  "op": "analyze",
  "assetIds": [
    "ast_1a2b"
  ],
  "prompt": "Is there a person in the picture? Describe what they are wearing."
}

resize

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Scale to a box. Keeps aspect ratio unless fit=fill. Crop to an exact box with fit=cover, and let gravity=attention choose what to keep; fit=contain pads to the exact box instead.

ParameterTypeWhat it does
widthintegerTarget width in px (one of width/height required).
heightintegerTarget height in px.
fitstringcover · contain · fill · inside · outside default inside
gravitystringWhich part cover keeps, or where contain places the image: the nine compass points, or attention / entropy to pick the busiest region. Only meaningful with fit=cover or fit=contain. default centre
backgroundstringThe letterbox colour for fit=contain. Default: transparent, or white when the result is a JPEG. Only meaningful with fit=contain.
withoutEnlargementbooleanNever upscale. With fit=cover or fill and a box bigger than the image, the box shrinks to the biggest one of the same shape the image covers (1080×1350 on a 1024×1024 image → 819×1024): the output is smaller than asked, never a different shape. false enlarges to the exact box. default true
matchOrientationbooleanTurn the box to the image's orientation first: a portrait image asked for 1800×1200 is resized to 1200×1800, a landscape one to 1800×1200 — one step for a print size or a frame, whichever way the photo was shot. Judged on the image as the resize receives it (upright, after any rotate or crop before it); a square image or a square box is never turned. Needs width and height. default false
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "resize",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "width": 1200
  }
}

convert

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Change the file format.

ParameterTypeWhat it does
formatstringwebp · jpeg · png · avif · gif · tiff (required)
qualityinteger1–100, lossy formats only. default 82
backgroundstringWhat transparency becomes when the target format has no alpha channel (jpeg). Ignored by formats that keep it. default #ffffff
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "convert",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "format": "webp",
    "quality": 82
  }
}

compress

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Re-encode smaller — at a quality target, or at a file-size target.

ParameterTypeWhat it does
qualityinteger1–100. default 75
formatstringwebp · jpeg · png · avif default webp
maxBytesintegerHit this file size instead of a quality: the encoder is run again at lower quality until the result fits. Lossy formats only (webp · jpeg · avif). A size this image cannot reach is 400 invalid_param, never a blurry near-miss.
backgroundstringWhat transparency becomes when writing jpeg. default #ffffff
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "compress",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "maxBytes": 200000
  }
}

crop

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Cut a rectangle out of the image at an exact offset. To crop to a size without knowing the offset, use resize with fit=cover and a gravity.

ParameterTypeWhat it does
leftintegerOffset from the left edge. default 0
topintegerOffset from the top edge. default 0
widthintegerCrop width (required).
heightintegerCrop height (required).
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "crop",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "left": 100,
    "top": 50,
    "width": 800,
    "height": 800
  }
}

pad

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Extend the canvas with a background colour (transparent by default).

ParameterTypeWhat it does
topinteger default 0
bottominteger default 0
leftinteger default 0
rightinteger default 0
backgroundstringCSS colour. Default: transparent, or white when the result is a JPEG, which has no alpha channel.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "pad",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "top": 40,
    "bottom": 40,
    "left": 40,
    "right": 40,
    "background": "#ffffff"
  }
}

grayscale

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Convert to greyscale.

ParameterTypeWhat it does
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "grayscale",
  "assetIds": [
    "ast_1a2b"
  ]
}

rotate

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Turn the image. Multiples of 90 are lossless; any other angle fills the corners.

ParameterTypeWhat it does
angleintegerDegrees clockwise, -360 to 360 (required).
backgroundstringCorner fill for angles that are not a multiple of 90. Default: transparent, or white when the result is a JPEG.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "rotate",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "angle": 90
  }
}

flip

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Mirror top to bottom.

ParameterTypeWhat it does
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "flip",
  "assetIds": [
    "ast_1a2b"
  ]
}

flop

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Mirror left to right.

ParameterTypeWhat it does
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "flop",
  "assetIds": [
    "ast_1a2b"
  ]
}

trim

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Cut away a uniform border — the whitespace around a cut-out, or a solid matte.

ParameterTypeWhat it does
thresholdintegerHow different from the border colour a pixel must be to be kept, 0–255. default 10
backgroundstringThe border colour. Defaults to the top-left pixel.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "trim",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "threshold": 10
  }
}

overlay

deterministic job type process · typically ~3 s per item

Put another image on top — a logo, a watermark. Job only: it reads a stored layer, and the synchronous endpoints hold no credential for one.

ParameterTypeWhat it does
layerAssetIdstringAsset id of the layer, one of your own (required).
gravitystringWhere it sits: the nine compass points. default southeast
scalenumberThe layer's share of the base width, above 0 and at most 1. A layer that would come out taller than the image is scaled down to fit. default 0.25
opacitynumberAbove 0 and at most 1. default 1
marginintegerDistance from the edge, in px. default 16
tilebooleanRepeat across the whole image; gravity and margin then do nothing. default false
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "overlay",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "layerAssetId": "ast_9z8y",
    "gravity": "southeast"
  }
}

caption

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Write a line of text on the image — a price, a title, a label. For anything with a layout, use render_template.

ParameterTypeWhat it does
textstringThe text (required). Written as content, not markup.
fontstringA font family installed in the worker image. default sans
sizeintegerPoint size, at most: text that would not fit the image (a long word, a small picture) is scaled down to fit. Defaults to a twentieth of the image width.
colorstringCSS colour. default #ffffff
backgroundstringA plate behind the text. Without one, light text on a light photo is unreadable.
gravitystringThe nine compass points. default southeast
marginintegerDistance from the edge, in px. default 16
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "caption",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "text": "$39",
    "gravity": "southeast"
  }
}

mask

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Cut the image to a shape — a round avatar, a card with rounded corners. The result carries transparency, so it needs a format that has an alpha channel.

ParameterTypeWhat it does
shapestringcircle · rounded default circle
radiusintegerCorner radius in px, required for shape=rounded.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "mask",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "shape": "circle"
  }
}

blur_region

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Blur or pixelate one rectangle — a face, a licence plate, an address.

ParameterTypeWhat it does
leftintegerOffset from the left edge. default 0
topintegerOffset from the top edge. default 0
widthintegerRegion width (required).
heightintegerRegion height (required).
sigmanumberBlur strength, 0.3–1000. default 12
pixelateintegerBlock size in px. Blocks instead of a blur — a blur can be sharpened back, blocks cannot.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "blur_region",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "left": 120,
    "top": 80,
    "width": 200,
    "height": 60
  }
}

adjust

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Brightness, contrast, saturation, hue — the batch correction a shelf of product photos needs. At least one of them is required.

ParameterTypeWhat it does
brightnessnumberMultiplier, 0.1–10. 1 is unchanged.
saturationnumberMultiplier, 0–10. 0 is greyscale.
huenumberRotation in degrees, -360 to 360.
lightnessnumberAdded, -100 to 100.
contrastnumberMultiplier around mid-grey, 0.1–10. 1 is unchanged.
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "adjust",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "brightness": 1.1
  }
}

flatten

deterministic job type process · sync POST /api/v1/images/transform · typically ~3 s per item

Composite transparency onto a solid colour — what a marketplace means by "white background".

ParameterTypeWhat it does
backgroundstringCSS colour. default #ffffff
metadatastringstrip (default) removes EXIF, ICC and XMP — including the GPS coordinates a phone writes. keep carries all of it into the output. default strip
framestringfirst (default) reads only the first frame of an animation — which is why a resized GIF used to stop moving. all keeps every frame, and then the output format has to be one that animates (gif or webp). default first
colorSpacestringsrgb converts the pixels to sRGB and attaches the profile. There is one value because there is one colour space the web assumes: a CMYK or Adobe RGB original published as-is goes grey, and this is the only fix.
densityintegerDots per inch to rasterise a VECTOR input (SVG) at. A drawing has no pixels until something picks a scale, and 72 is what libvips picks; raise it to draw the vector larger instead of upscaling a small raster. Ignored for photographs. default 72

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "flatten",
  "assetIds": [
    "ast_1a2b"
  ],
  "parameters": {
    "background": "#ffffff"
  }
}

render_template

deterministic job type render · sync POST /api/v1/images/render

Render an HTML/CSS template once per item of data → one PNG asset per item. Templates: POST /api/v1/templates ({{ var }} placeholders, HTML-escaped; {{{ var }}} raw).

ParameterTypeWhat it does
templateIdstringTemplate id from GET /api/v1/templates, or id@version to pin a version (required).
itemsarrayOne object of template variables per output image, 1–500 (required).

Deterministic op: unlimited on paid plans; Free counts it against the monthly allowance and pays each run past it from the balance. Free plan: 200 per month.

The request — the catalogue's example
{
  "op": "render_template",
  "templateId": "builtin-template-og-image",
  "items": [
    {
      "title": "Hello"
    }
  ]
}

read_metadata

sync sync POST /api/v1/images/metadata · answers with JSON, not an image

EXIF, GPS, dimensions, hash and mime type — extracted when the asset was ingested; no job needed.

Answered by GET /api/v1/assets/{id} (fields image, metadata)

Free. Read on every asset, any plan.

Models

An AI op runs on a model. Its entry names the default (defaultModel, priced under pricing.defaultModel); a request's own model overrides it, but only with a model sold for that op — one whose categories include the entry's modelCategory. Any other is 400 invalid_param, with the models that fit in details.allowed. Models come in two modes: ai_image for generate, edit and the other image ops, and analyze for the analyze op.

Every model is priced per image in USD: the provider's list price times 1 + markup, and credits are that times creditsPerUsd — all three are in the entry. providerPrice is the provider's price as we last recorded it, not a live quote. An analyze model's price is per image too, fixed before it runs: its providerPrice is the most one request can use — the image, a prompt and schema at their limits, and maxOutputTokens — so a typical request costs us well under it, and the dry run, the job and the charge are the one number.

read it fromwhat you get
GET https://api.imagestep.dev/api/pricing/image-modelsevery model with priceFrom / priceRange and providerPrice — public, no key
GET /api/v1/ai-models?mode=ai_image|analyzethe same models with an API key, plus what a call needs to pick one: its categories, its own parameters (the keys an AI op's parameters takes, with defaults and allowed values) and max_input_edge
imagestep models listthe CLI; -m analyze for the analyze models
imagestep://models/{mode}the MCP resource, for a client that cannot curl

Where a price is a range, the provider's is too: an upscaler billed by output megapixel costs more at 4× than at 2×, an image model more at 4K than at 1K. priceFrom is the cheapest setting, which is not always what a call that sets nothing gets — the dry run prices the parameters you actually send. A model nobody can price-check is not in the catalogue. The human-readable table, plans included, is /pricing.

Combining ops

One op per call. Two ops in a row are a preset: a saved, versioned list of steps that runs as one job (or, when every step can run synchronously, in one synchronous call) — or the same steps sent inline, when a run needs no name or version (without saving them). Several images with one op are one job with several assetIds; several sizes from one image are one job with variants, which a deterministic op takes. What an op cannot express — a sharpen, a colour matrix — a preset step names from the engine's step registry.

What an entry carries

Every entry is the whole contract for its op, so nothing has to be guessed and nothing is hard-coded on a client. This is remove_bg's, as the service answers it:

{
  "op": "remove_bg",
  "kind": "ai",
  "produces": "image",
  "description": "Cut the subject out; transparent PNG result.",
  "jobType": "ai-edit",
  "requiresAssets": true,
  "requiresPrompt": false,
  "defaultModel": "fal-ai/bria/background/remove",
  "params": {
    "model": {
      "type": "string",
      "default": "fal-ai/bria/background/remove",
      "description": "A model whose categories include 'background_removal' — GET /api/v1/ai-models lists them. Any other model is 400 invalid_param, with the ones that fit in details.allowed."
    }
  },
  "pricing": {
    "basis": "per_item",
    "summary": "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.",
    "defaultModel": {
      "id": "fal-ai/bria/background/remove",
      "name": "Fal.ai: Bria RMBG 2.0",
      "series": "Fal.ai",
      "description": "Commercial-grade background removal trained exclusively on licensed data. Best for professional editing tasks requiring seamless background removal.",
      "categories": [
        "background_removal"
      ],
      "priceFrom": "0.0227",
      "priceRange": "$0.0227",
      "providerPrice": "0.018"
    },
    "markup": 0.26,
    "creditsPerUsd": 10000,
    "exactPrice": "POST /api/v1/jobs?dryRun=true"
  },
  "maxInputEdge": 2048,
  "modelCategory": "background_removal",
  "example": {
    "op": "remove_bg",
    "assetIds": [
      "ast_1a2b"
    ]
  },
  "typicalSeconds": 5
}
fieldtypemeaning
opstringthe name you send
kindai | deterministic | syncwhich of the three kinds above
descriptionstringone line on what it does — what a dropdown or a tool list shows
jobTypestringthe job type it submits as — the service derives it, you never send it alongside op
paramsobjectname → {type, default, description}. For a deterministic op these are the keys of parameters, a closed set: any other key is 400 invalid_param naming it, with the ones it takes in details.allowed. For an AI op they are fields of the body — prompt, count, model — plus parameters, whose keys are the model's (GET /api/v1/ai-models): a key or value the model does not declare is 400 invalid_param too. render_template's are fields of the body too
requiresAssets · requiresPromptbooleanwhether it takes input images (generate and render_template do not) and whether a prompt is mandatory (generate, edit) or optional with a default (analyze)
producesimage | jsonwhat one step of it hands to the step after it: an image, or json. Read it with requiresAssets before you compose a chain — an op that answers with json is its last step, and an op that reads no image is not a step of one at all (presets → the order the steps are in)
defaultModelstringAI ops: the model that runs when a call names none
modelCategorystringAI ops: the category a model must carry (GET /api/v1/ai-models → categories) to run this op; a model from another one is 400 invalid_param with the ones that fit in details.allowed
maxInputEdgeintegerAI ops: the longest edge, in px, an input image is scaled down to (never up) before the default model sees it. For remove_bg and colorize it is also the largest result; upscale and restore_face multiply it by their scale factor; generate and edit write the size parameters.imageSize asks for
defaultPrompt · defaultSchemastring · objectanalyze: the prompt it asks and the JSON schema it answers in when a call gives neither
syncEndpointstringthe synchronous endpoint that runs it without a job, or absent — every deterministic op has one but overlay, because the sync lane holds no credential for a second stored image
endpointstringa sync op: where its answer already is without sending bytes — read_metadata's is the asset record
exampleobjectone complete POST /api/v1/jobs body that runs the op — its required parameters and a placeholder asset id — checked by the validator a submit goes through. Copy it and swap the id; each entry above shows it, and every surface posts this same body. A sync op has none
typicalSecondsintegerhow long ONE item usually takes as a job, submit to done, on the default model — so you can size a wait (jobs → waiting) from the catalogue instead of guessing. Measured on production; a hint, not a promise, and absent where nobody has measured yet
pricingOpPricingbasis (per_item · process_quota · free) and a one-line summary; for an AI op the default model's price record (id, priceFrom, priceRange, providerPrice), markup and creditsPerUsd; for a deterministic op processLimit, the monthly allowance per plan (-1 is unlimited), and overageCredits, what a run past it costs on a plan that has one; exactPrice names the dry run