Remove background from images in Node.js with the LassoCut API
Node.js 18 and later have fetch, FormData and Blob built in, so removing a background needs no package at all. This guide starts with one tested call, then runs a whole folder through a small promise pool of three concurrent requests with retries, and explains what to change in an existing axios or undici setup.
Quickstart
Every request needs your key in the X-Api-Key header and exactly one image source. This example uses size=preview, which is covered by your account's 50 free previews a month (up to 10 a day). Node.js 18 or later has fetch and FormData built in, no packages to install.
import { readFile, writeFile } from "node:fs/promises";
const form = new FormData();
form.append("image_file", new Blob([await readFile("photo.jpg")]), "photo.jpg");
form.append("size", "preview");
const res = await fetch("https://api.lassocut.com/v1.0/removebg", {
method: "POST",
headers: { "X-Api-Key": process.env.LASSOCUT_API_KEY },
body: form,
});
if (!res.ok) throw new Error(JSON.stringify(await res.json()));
console.log("credits charged:", res.headers.get("x-credits-charged"));
await writeFile("no-bg.png", Buffer.from(await res.arrayBuffer()));
Tested against the production API on 27 September 2026 with this exact request; it returned a valid PNG.
Run a folder with a promise pool of 3
Promise.all over every file would fire hundreds of uploads at once and hit the rate limit. A pool of three workers pulling from a shared index keeps exactly three requests in flight. Save it as cut.mjs (top-level await needs an ES module) and run node cut.mjs.
import { readdir, readFile, writeFile, mkdir, access } from "node:fs/promises";
import path from "node:path";
const API = "https://api.lassocut.com/v1.0/removebg";
const SRC = "photos", OUT = "out", PARALLEL = 3;
await mkdir(OUT, { recursive: true });
const exists = (p) => access(p).then(() => true, () => false);
const sleep = (s) => new Promise((r) => setTimeout(r, s * 1000));
async function cut(name) {
const dest = path.join(OUT, path.parse(name).name + ".png");
if (await exists(dest)) return `skip ${name}`;
const bytes = await readFile(path.join(SRC, name));
for (let attempt = 1; ; attempt++) {
const form = new FormData();
form.append("image_file", new Blob([bytes]), name);
form.append("size", "full");
form.append("format", "png");
const res = await fetch(API, {
method: "POST",
headers: { "X-Api-Key": process.env.LASSOCUT_API_KEY },
body: form,
});
if ((res.status === 429 || res.status === 503) && attempt < 5) {
await res.body?.cancel(); // free the connection
await sleep(Number(res.headers.get("retry-after")) || 2 ** attempt);
continue;
}
if (!res.ok) return `failed ${name}: ${res.status} ${await res.text()}`;
await writeFile(dest, Buffer.from(await res.arrayBuffer()));
return `ok ${name} (${res.headers.get("x-credits-charged")} credit)`;
}
}
const files = (await readdir(SRC)).filter((f) => /\.(jpe?g|png|webp)$/i.test(f));
let next = 0;
async function worker() {
while (next < files.length) {
const name = files[next++];
console.log(await cut(name).catch((e) => `failed ${name}: ${e.message}`));
}
}
await Promise.all(Array.from({ length: PARALLEL }, worker));
- Retries:
429 Rate limit exceededand503are retried up to four times. The wait comes from theRetry-Afterheader, in seconds, or doubles from 2 seconds when it is missing.res.body?.cancel()releases the connection before waiting. - Skip if done: an image whose
out/<name>.pngexists is not sent again, so a run stopped halfway, or by a402 Insufficient credits, resumes where it left off. - One failure does not stop the pool: each worker catches its own errors, prints the reason and moves on to the next file.
- Headers:
x-credits-chargedgives the cost of each call;x-ratelimit-remainingwhat is left of the 500 megapixel-images per minute.
The script was run on Node.js 22 against a local test server that answers 429 with Retry-After before succeeding; at most three requests were in flight at any time.
Point an existing Node.js client at LassoCut
Most Node.js remove.bg wrappers hardcode api.remove.bg internally and don't expose a base URL to change, so there is often nothing to reconfigure. The fetch example above is usually the fastest way to switch: it is a dozen lines with no dependency to audit.
If you use a generic HTTP client library that already sends this header and this multipart body to remove.bg (axios, undici, node-fetch), point its base URL at https://api.lassocut.com/v1.0 and keep the same X-Api-Key header; no other code changes are needed.
Useful options
| Field | Example | Description |
|---|---|---|
size | preview, full, auto | Preview costs 0.25 credit; full and 50MP cost 1 credit; auto picks the largest size your balance allows. |
format | png, jpg, webp | Auto (default) returns PNG when the result has transparency, JPG otherwise. |
type | person, product, car | Selects the model. Auto (default) detects the subject. |
crop, crop_margin | true, "30px" | Crops the result to the subject, with an optional margin. |
bg_color | "81d4fa" | Fills the background with a hex colour, a colour name, or hex with alpha. |
Full list, including framing, shadows and background images: API reference.
Marketplace preset and shadow
A marketplace preset frames the cut-out on the canvas, white background and file size that marketplace expects, and shadow_type=cast adds a cast shadow. The response header X-Compliance says whether the result meets the rules LassoCut checks. With a preset, crop, crop_margin, scale and position are ignored. See marketplace presets.
const form = new FormData();
form.append("image_file", new Blob([await readFile("product.jpg")]), "product.jpg");
form.append("preset", "amazon");
form.append("shadow_type", "cast");
form.append("shadow_angle", "45");
const res = await fetch("https://api.lassocut.com/v1.0/removebg", {
method: "POST",
headers: { "X-Api-Key": process.env.LASSOCUT_API_KEY },
body: form,
});
if (!res.ok) throw new Error(JSON.stringify(await res.json()));
console.log("compliance:", res.headers.get("x-compliance"));
await writeFile("amazon.jpg", Buffer.from(await res.arrayBuffer()));
Common errors
Errors return JSON: {"errors": [{"code": "…", "title": "…"}]}, available from await res.json() when res.ok is false. Nothing is charged on an error.
| Status | When | code |
|---|---|---|
| 400 | No image, or two sources given | missing_source, multiple_sources |
| 402 | Not enough credits for this size | title Insufficient credits |
| 403 | Missing or wrong X-Api-Key | titles Missing API Key, API Key invalid |
| 429 | Rate limit reached | title Rate limit exceeded, with Retry-After |
Full list: API reference.
Next steps
Read the full API reference for every parameter and header, or the migration guide if you're moving code that already calls remove.bg. No key yet? Create a free account, or try LassoCut on your image first, no key required.