Remove an image background
remove_bg cuts the subject out of a photo and hands back a transparent PNG. It is one op of the catalogue, which means one call, a job handle you can poll, cancel or subscribe to, and a price you can read before you spend it.
What it does
A model finds the subject and writes everything else to transparency. The output is a new asset — a PNG with an alpha channel, the size of your input up to the model's input cap — so the original stays where it was and stays readable. No job overwrites the asset it read, so a pipeline can keep both ids.
Reach for it when a program has to make product shots, avatars or stickers usable on any background. Do not reach for it to erase one object from a scene — that is edit, with an instruction. Do not reach for it to trim a uniform border either: that is trim, which is deterministic, free on a paid plan and runs while you wait.
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.removeBg 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.removeBg("ast_1a2b", { wait: true });
const [out] = await client.jobs.outputs(job);Python
# pip install imagestep
job = client.ops.remove_bg("ast_1a2b", wait=True)
[out] = client.jobs.outputs(job)CLI
imagestep jobs submit --op remove_bg --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":"remove_bg","assetIds":["ast_1a2b"],"wait":30}'MCP
# tool call
transform {"op":"remove_bg","asset_ids":["ast_1a2b"]}n8n
{
"nodes": [
{
"parameters": {
"resource": "op",
"operation": "run",
"op": "remove_bg",
"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 remove_bg 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/remove_bg — 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.
remove_bg
ai job type ai-edit · typically ~5 s per item · input scaled to ≤ 2048 px on the long edgeCut the subject out; transparent PNG result.
| Parameter | Type | What it does |
|---|---|---|
| model | string | 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. 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.
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
The cutout is rarely the end of the job. Two things follow it often enough to be worth writing down, and both stay one job rather than several round trips:
Cut out, then put the subject on white — two steps saved as a preset, one job per batch:
{
"name": "Packshot",
"slug": "packshot",
"description": "Cut the subject out and put it on marketplace white.",
"steps": [
{
"op": "remove_bg"
},
{
"op": "flatten",
"parameters": {
"background": "#ffffff"
}
}
]
}JavaScript
import { readFile } from "node:fs/promises";
const preset = await client.presets.create(JSON.parse(await readFile("packshot.json", "utf8")));
console.log(preset.slug, preset.version); // "packshot" 1Python
import json
preset = client.presets.create(json.load(open("packshot.json")))
print(preset["slug"], preset["version"]) # "packshot" 1CLI
imagestep preset create -f packshot.json -o jsoncurl
curl -s https://api.imagestep.dev/api/v1/presets -H "Authorization: ApiKey $IMAGESTEP_API_KEY" -H "Content-Type: application/json" -d @packshot.jsonMCP
# tool call — an agent has no disk to read, so the document travels in the call
save_preset {"name":"Packshot","slug":"packshot","description":"Cut the subject out and put it on marketplace white.","steps":[{"op":"remove_bg"},{"op":"flatten","parameters":{"background":"#ffffff"}}]}JavaScript
const job = await client.presets.run("packshot", ["<asset-id>"], { wait: true });
const outputs = await client.jobs.outputs(job);Python
job = client.presets.run("packshot", ["<asset-id>"], wait=True)
outputs = client.jobs.outputs(job)CLI
imagestep jobs submit --preset-id packshot --asset-ids <asset-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":"packshot","assetIds":["<asset-id>"],"wait":30}'MCP
# tool call
run_preset {"preset":"packshot","asset_ids":["<asset-id>"]}Cut out once, then every size the storefront needs — variants makes one asset per entry, in one job:
JavaScript
const job = await client.ops.run("resize", {"assetIds":["<cutout-id>"],"variants":[{"name":"grid","parameters":{"width":600,"height":600,"fit":"contain"}},{"name":"hero","parameters":{"width":1600}}],"wait":true});Python
job = client.ops.run("resize", asset_ids=["<cutout-id>"], variants=[{"name":"grid","parameters":{"width":600,"height":600,"fit":"contain"}},{"name":"hero","parameters":{"width":1600}}], wait=True)CLI
imagestep jobs submit --op resize --asset-ids <cutout-id> --variants '[{"name":"grid","parameters":{"width":600,"height":600,"fit":"contain"}},{"name":"hero","parameters":{"width":1600}}]' --wait -o jsoncurl
curl -s https://api.imagestep.dev/api/v1/jobs -H "Authorization: ApiKey $IMAGESTEP_API_KEY" -H "Content-Type: application/json" \
-d '{"op":"resize","assetIds":["<cutout-id>"],"variants":[{"name":"grid","parameters":{"width":600,"height":600,"fit":"contain"}},{"name":"hero","parameters":{"width":1600}}]}'MCP
# tool call
transform {"op":"resize","asset_ids":["<cutout-id>"],"variants":[{"name":"grid","parameters":{"width":600,"height":600,"fit":"contain"}},{"name":"hero","parameters":{"width":1600}}]}To hand the result to something outside ImageStep, publish the output asset: it gets a stable URL on the CDN at full size, which is what an agent or an n8n node passes along. A batch of hundreds is still one job — send every id in assetIds (up to 10,000) and subscribe to the job.completed webhook instead of polling.
Limits
The default model is handed at most 2048 px on the long edge — a larger photo is scaled down first — so that is the largest cutout it returns, whatever the original's size. Hair, glass and motion blur are where every background remover is judged, and where the models in the catalogue differ most — if the default disappoints on your images, try another model before concluding the op cannot do it. A busy scene with no obvious subject has no right answer; the model will pick one. Items that fail inside a job fail on their own, carry an error code and a retryable flag, and are not charged, so one bad image does not cost you the batch.
FAQ
- What does the result look like?
- A new asset holding a PNG with an alpha channel — the subject on transparency, at the size of the image the model was handed: your input's own, unless its long edge is over the catalogue's maxInputEdge, which it is scaled down to first. The original is untouched.
- Can I get the cutout back in the same HTTP response?
- Its handle, yes: send wait (up to 60 s) with the submit and a one-image job answers when it is done, the cutout's asset id on its item — typically a few seconds. Not the bytes: remove_bg is an AI op, and every AI op is a job, because a model call needs an owner for its retry and its refund. Deterministic ops (resize, convert, crop) do have a synchronous, bytes-back form.
- How do I put the subject on a white background instead?
- Run remove_bg, then flatten with a background colour. Two ops in a row are a preset — save the pair once and run it by slug as a single job.
- What does it cost?
- Credits per image at the model's published per-image price. The dry run gives the exact number before you spend anything.
- Which model runs it?
- fal-ai/bria/background/remove by default; pass model to pick another background remover — any model whose categories include background_removal (GET /api/v1/ai-models). Another model is 400 invalid_param, with the ones that fit in details.allowed.