Removing the background from product photos by API
One call, one flat rate, and a transparent PNG. Where it works well, where it fails, and the review step that keeps bad cutouts off your catalogue.
Background removal is the image operation that used to mean a designer and a pen tool. POST /v1/image/background_remove on Infrai takes an image — a URL, base64 data or a stored image_id — and returns a cutout, billed per call at a flat rate rather than per pixel.
It’s also the operation most likely to produce something subtly wrong, so the interesting engineering is the review step rather than the call.
The call
curl -sS -X POST "https://api.infrai.cc/v1/image/background_remove" \
-H "Authorization: Bearer ${INFRAI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"image": "img_2fVc8nRqLmT4xBzY", "format": "png"}'
{
"ok": true,
"data": {
"image_id": "img_9wQ1zV6pLkS3dHyB",
"url": "https://files.infrai.cc/image/9wQ1zV6pLkS3dHyB.png",
"format": "png",
"width": 1600,
"height": 1600,
"size_bytes": 842104,
"sha256": "9f2c41bd7a9e8c0b5d3e1f7a2b8d4c6e0a9f3b1d5c7e2a4f6b8d0c2e4a6f8b0d",
"original_sha256": "3a9d545444dc7a02e085889a3f7130789f2c41bd7a9e8c0b5d3e1f7a2b8d4c6e",
"ops_applied": ["background_remove"],
"cost_usd": 0.05,
"created_at": "2026-09-21T03:50:00Z"
}
}
Ask for png explicitly. A cutout needs an alpha channel, and format: "auto" may hand you something that flattens the transparency you just paid for — webp also carries alpha if size matters more than compatibility.
cost_usd on the response is the actual charge for this call. Log it against the product record; at a per-call rate, a catalogue of ten thousand photos is a number your finance team will ask about.
Where it works and where it doesn’t
| Subject | Result |
|---|---|
| Product on a plain backdrop | excellent — this is the designed case |
| Product with a hard shadow | usually good; the shadow may go with the background |
| Hair, fur, feathers | soft edges, visible fringing |
| Glass, bottles, anything transparent | poor — the model can’t tell background from contents |
| Mesh, lace, chain-link | poor |
| Product against a similarly-coloured backdrop | unpredictable |
Transparent products are the honest failure.
A perfume bottle cut out of its background takes the background through the glass with it, because from the model’s point of view the pixels inside the bottle genuinely are background — and no parameter fixes that, because it isn’t a tuning problem. Those photos need a human with a mask, or a reshoot against a backdrop chosen to survive the operation.
Review before publishing
The check that catches most bad cutouts is cheap: compare the output’s alpha coverage to what you’d expect. A cutout that removed almost nothing, or almost everything, is wrong.
import os
import requests
API = "https://api.infrai.cc"
KEY = os.environ["INFRAI_API_KEY"]
SESSION = requests.Session()
SESSION.headers.update({"Authorization": f"Bearer {KEY}", "Content-Type": "application/json"})
def cutout(image: str) -> dict:
resp = SESSION.post(f"{API}/v1/image/background_remove",
json={"image": image, "format": "png"}, timeout=180)
body = resp.json()
if not body.get("ok"):
raise RuntimeError(body["error"]["code"])
return body["data"]
def metadata(image: str) -> dict:
resp = SESSION.post(f"{API}/v1/image/metadata", json={"image": image}, timeout=60)
resp.raise_for_status()
return resp.json()["data"]
def cutout_with_review(image_id: str, min_shrink: float = 0.05,
max_shrink: float = 0.85) -> dict:
"""Use the size change as a cheap sanity signal. A PNG cutout that is barely
smaller than the original probably removed nothing; one that collapsed to
almost nothing probably removed the product. Neither should reach a catalogue
without a human looking, and a size ratio catches both for the price of one
extra metadata read."""
before = metadata(image_id)
result = cutout(image_id)
after = metadata(result["image_id"])
if not after.get("has_alpha"):
return {"image_id": result["image_id"], "review": "no alpha channel — wrong format?"}
ratio = 1 - (result["size_bytes"] / max(1, before["size_bytes"]))
verdict = "ok"
if ratio < min_shrink:
verdict = "suspiciously similar to the original — check it removed anything"
elif ratio > max_shrink:
verdict = "collapsed — check the product survived"
return {"image_id": result["image_id"], "url": result["url"],
"cost_usd": result.get("cost_usd"), "shrink_ratio": round(ratio, 3),
"review": verdict, "needs_human": verdict != "ok"}
if __name__ == "__main__":
print(cutout_with_review(os.environ["IMAGE_ID"]))
That heuristic is crude and it catches the two failures that actually reach production. Route needs_human to a review queue with POST /v1/queue/publish rather than publishing optimistically.
Composite onto the backdrop you want
A cutout is rarely the final asset. Put it on a white background for the catalogue and a branded one for social:
curl -sS -X POST "https://api.infrai.cc/v1/image/process" \
-H "Authorization: Bearer ${INFRAI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{
"image": "img_9wQ1zV6pLkS3dHyB",
"ops": [{"resize": {"width": 1200, "height": 1200, "fit": "contain"}}, {"compress": {"quality": 86}}],
"format": "webp",
"store": true
}'
fit: "contain" keeps the whole product visible with padding rather than cropping it, which is what a catalogue tile wants.
Doing a catalogue
Ten thousand photos at a per-call rate is a number worth planning rather than discovering. Two controls help: POST /v1/image/batch/submit takes many items with a webhook_url so you’re not managing ten thousand individual calls, and PUT /v1/account/budget/set bounds the worst case if a loop misbehaves.
Both are on the same key as the processing, along with the storage the results land in and the GET /v1/account/usage that prices the whole catalogue run — which for a bulk job is the difference between one integration and three.
Limitations
There’s no mask output, no edge-refinement parameters and no way to nudge a poor cutout — it’s one call with one result, so a bad result is a candidate for manual work rather than for tuning. There’s also no compositing onto an arbitrary background image in a single operation.
Cloudinary’s AI background removal and imgix’s processing pipeline both offer more control here, including masks and generative fill, and a dedicated retouching service does the transparent-products case properly. Background removal bills per call at a rate live in GET /v1/discovery/image.background_remove (verified 2026-09-21), and platform rates drift downward as vendor contracts improve.