logobgbuster
Blog

Background removal API: from first request to production

What the quickstarts don't cover: choosing between URL and raw output, files vs URLs as input, which errors to retry, staying under the rate limit, and chaining edits into one request.

The quickstarts get you from zero to a first transparent PNG in a few minutes. This guide covers what comes next: the decisions and failure modes you hit when background removal runs in production instead of in a terminal.

Everything below uses the bgbuster API at https://api.bgbuster.com, authenticated with an X-Api-Key header.

The two endpoints

EndpointUse it for
POST /images/remove-backgroundA plain cutout. Outputs PNG or WebP, with optional trim.
POST /images/editA background removal combined with other edits (white background, resize, rotate, and so on) in one request.

Both take a multipart form and cost one credit per successful request.

Input: upload a file or pass a URL

The input field accepts either an uploaded file or a publicly reachable image URL. Supported types are JPEG, PNG, WebP, GIF, SVG, and AVIF, up to 20 MB.

Use a URL when the image is already hosted somewhere public, like a CDN or a bucket with public reads. Your server never touches the bytes.

Upload the file when the image is on disk, private, or on a host that blocks automated requests. Some CDNs refuse requests that don't come from a browser. You'll see that as 403 IMAGE_FORBIDDEN, and the fix is to download the image yourself and upload it.

Output: a URL or the raw bytes

output=url is the default. It returns JSON:

{ "url": "https://..." }

The URL is temporary and stays valid for one day. That's fine for showing a result to a user right away or passing it to a job that runs soon after. It won't work as permanent storage.

output=raw returns the image bytes directly, with the matching Content-Type. Use it when you store results in your own bucket anyway. It saves you a second download.

Rule of thumb: if the result ends up in your storage, use raw. If a client displays it once, use url.

Chaining edits in one request

/images/edit applies a list of operations in the order you send them, up to 20 per request. Send the list as a JSON string in the form:

curl -X POST https://api.bgbuster.com/images/edit \
  -H "X-Api-Key: $BGBUSTER_API_KEY" \
  -F "input=https://example.com/photo.jpg" \
  -F 'operations=[{"resize":{"width":1600}},{"removeBackground":true},{"background":"#ffffff"}]' \
  -F "format=jpeg" \
  -F "quality=85"
OperationValue
removeBackgroundtrue. At most once per request, and skipped if the image is already transparent.
backgroundA hex code, rgb()/rgba(), hsl()/hsla() (comma-separated), or a named color. Fills the transparent areas at that point in the pipeline.
resize{ "width", "height", "fit" }, where fit is contain, cover, scale-down, or crop
trimtrue. Trims a uniform-color border.
rotate90, 180, or 270 (clockwise)
flip"h", "v", or "hv"
blur0 to 250
brightness, contrast, saturationA multiplier, where 1.0 means no change

Order matters. background only fills pixels that are transparent at that point, so it has to come after removeBackground. Resizing first can also speed things up, since there are fewer pixels to segment. /images/edit supports png, webp, jpeg, gif, and avif output, and quality (1–100) applies to JPEG and WebP.

Errors: which to retry

Errors come back as JSON with a code and a message, for example {"code":"INSUFFICIENT_BALANCE","message":"Insufficient balance"}. Decide whether to retry from the status code, not the code field: anything in the 4xx range except 429 will fail the same way again.

StatusCodeRetry?
400IMAGE_TOO_LARGE, IMAGE_DECODE_ERROR, INVALID_IMAGE_URLNo. Fix the input.
400UPSTREAM_ERRORMaybe. The input URL couldn't be fetched, so check that it's reachable.
401UNAUTHORIZEDNo. The key is missing or invalid.
402INSUFFICIENT_BALANCENo. You're out of credits; alert a human.
403IMAGE_FORBIDDENNo. Upload the file instead of passing the URL.
429TOO_MANY_REQUESTSYes, after a short delay.
400IMAGE_TRANSFORMATION_ERRORNo. The file couldn't be processed, usually because it's corrupt or not really an image.
503IMAGE_TRANSFORMATION_UNAVAILABLEYes, after a short delay. Processing hit a temporary problem.

Failed requests don't cost credits. A credit is only deducted after a successful transformation.

Here's a retry wrapper in Node.js that follows that table:

const RETRYABLE = new Set([429, 500, 502, 503, 504]);

async function removeBackground(input, { attempts = 4 } = {}) {
  for (let attempt = 1; ; attempt++) {
    const body = new FormData();
    body.append("input", input);
    body.append("output", "raw");

    const response = await fetch("https://api.bgbuster.com/images/remove-background", {
      method: "POST",
      headers: { "X-Api-Key": process.env.BGBUSTER_API_KEY },
      body
    });

    if (response.ok) {
      return Buffer.from(await response.arrayBuffer());
    }

    if (!RETRYABLE.has(response.status) || attempt === attempts) {
      throw new Error(`bgbuster ${response.status}: ${await response.text()}`);
    }

    // Exponential backoff with jitter: ~0.5s, 1s, 2s…
    const delay = 500 * 2 ** (attempt - 1) * (0.5 + Math.random());
    await new Promise((resolve) => setTimeout(resolve, delay));
  }
}

Rate limits

The API allows 400 requests per minute per IP address. Past that, you get 429 TOO_MANY_REQUESTS until the window resets.

For a web app handling one image per user action, you won't get close. For batch jobs, cap your concurrency instead of firing every request at once. The ecommerce guide has a worked example that processes a whole catalog while staying under the limit.

Use the SDK or plain HTTP

For JavaScript and TypeScript, @bgbuster/sdk gives you a typed client for both endpoints:

import { createClient } from "@bgbuster/sdk";

const client = createClient(process.env.BGBUSTER_API_KEY);

const { url } = await client.edit({
  input: "https://example.com/photo.jpg",
  operations: [{ removeBackground: true }, { background: "white" }],
  format: "jpeg"
});

Every other language just needs an HTTP client that can send multipart forms. The quickstarts include cURL, Python, PHP, Ruby, Java, Go, and C#.

Using it from an AI agent (MCP)

bgbuster also runs an MCP server at https://api.bgbuster.com/mcp, authenticated with the same X-Api-Key header. It exposes remove-background and edit as tools, plus create-image-upload so an agent can send a local file. The MCP page in your dashboard has copy-paste configs for Claude, Cursor, VS Code, and other clients.

Production checklist

  • Keep the API key on your server. Never ship it to a browser or mobile app.
  • Set a request timeout, and retry only on 429 and 5xx.
  • Alert on 402 so running out of credits doesn't fail silently.
  • Copy results you need to keep into your own storage. url output expires after one day.
  • Test on your hardest real images before launch: hair, glass, and subjects close in color to the background.

Further reading

Try the bgbuster API

Pay per image, no subscription, credits that never expire.

See pricing