State-of-the-art background removal API — open source, self-hostable, 40× cheaper than remove.bg.
Live API · Docs · Quick Start · API Reference · Self-hosting
Drop an image in. Get a transparent PNG out. ~200ms per call.
A production-grade background removal API powered by BiRefNet — the current SOTA on DIS5K, HRSOD, and COD benchmarks. Served on Modal's GPU infrastructure with scale-to-zero economics.
- SOTA quality — matches or beats remove.bg, Photoroom, and Pixelcut on hair, fur, fine detail
- Fast — ~200ms per image on a warm L4 GPU
- Cheap — ~$0.00005 per image raw compute cost (4,000x cheaper than remove.bg PAYG)
- MIT licensed — model weights and code, commercial use OK
- Self-hostable — deploy to your own Modal workspace in one command
Works alpha-preserving (PNG with transparent bg) OR opaque (solid color / remote image as new bg).
Live endpoint: https://useknockout--api.modal.run
Interactive docs: https://useknockout--api.modal.run/docs
Input → Output:
| Original | After |
|---|---|
| Complex hair | Clean wisps, no halo |
| Fur / pet photos | Soft edges preserved |
| Product shots | Sharp, clean cutout |
| Low-contrast subjects | Accurate separation |
During public beta, everyone shares this bearer token:
kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f
No signup. Just use it. We're free during beta. Paid tier launches soon — need your own key or higher limits? DM @useknockout.
curl -X POST "https://useknockout--api.modal.run/remove" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-F "file=@your-image.jpg" \
-o out.pngYou get a PNG with a transparent alpha channel. Done.
curl -X POST "https://useknockout--api.modal.run/remove-url" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-H "Content-Type: application/json" \
-d '{"url":"https://example.com/cat.jpg"}' \
-o out.png# solid color background
curl -X POST "https://useknockout--api.modal.run/replace-bg" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-F "file=@cat.jpg" \
-F "bg_color=#FF5733" \
-F "format=jpg" \
-o out.jpg
# use a remote image as the new background
curl -X POST "https://useknockout--api.modal.run/replace-bg" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-F "file=@cat.jpg" \
-F "bg_url=https://example.com/mountains.jpg" \
-o out.png# multipart batch
curl -X POST "https://useknockout--api.modal.run/remove-batch?format=png" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-F "files=@a.jpg" -F "files=@b.jpg" -F "files=@c.jpg"
# URL batch — JSON body
curl -X POST "https://useknockout--api.modal.run/remove-batch-url" \
-H "Authorization: Bearer kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" \
-H "Content-Type: application/json" \
-d '{"urls":["https://a.jpg","https://b.jpg"], "format":"png"}'Both return JSON: { "count": N, "format": "png", "results": [{ "success": true, "data_base64": "..." }, ...] }.
curl https://useknockout--api.modal.run/health
# {"status":"ok","model":"ZhengPeng7/BiRefNet"}Base URL: https://useknockout--api.modal.run
Remove the background from an uploaded image.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <API_TOKEN> |
Content-Type |
Auto | multipart/form-data (set by your client) |
Body — multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
binary | Yes | Image to process (JPEG, PNG, WebP). Max 25 MB. |
Query params
| Param | Type | Default | Description |
|---|---|---|---|
format |
string | png |
png (default) or webp. Both include alpha. |
Response — image/png or image/webp with a transparent background.
Fetch an image from a URL and remove its background.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <API_TOKEN> |
Content-Type |
Yes | application/json |
Body — JSON
{
"url": "https://example.com/image.jpg",
"format": "png"
}Response — same as /remove.
Remove the background and composite the subject onto a new background — solid color or a remote image.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <API_TOKEN> |
Content-Type |
Auto | multipart/form-data (set by your client) |
Body — multipart/form-data
| Field | Type | Required | Description |
|---|---|---|---|
file |
binary | Yes | Foreground image to process. Max 25 MB. |
bg_color |
string | No (default #FFFFFF) |
Hex color for the new background. Examples: #000000, #ff5733, #1a73e8. |
bg_url |
string | No | Remote URL of a background image. Takes precedence over bg_color. |
format |
string | No (default png) |
Output format: png, webp, or jpg (smallest, opaque only). |
Response — image/png, image/webp, or image/jpeg with the subject composited onto the new background. Edges are cleaned via closed-form foreground matting (no color spill, no halo).
Remove backgrounds from up to 10 images in one call.
Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Bearer <API_TOKEN> |
Content-Type |
Auto | multipart/form-data |
Body — multipart/form-data with repeated files fields.
Query params
| Param | Type | Default | Description |
|---|---|---|---|
format |
string | png |
png or webp. Applies to every result. |
Response — JSON:
{
"count": 3,
"format": "png",
"results": [
{ "filename": "a.jpg", "success": true, "format": "png", "size_bytes": 124503, "data_base64": "..." },
{ "filename": "b.jpg", "success": true, "format": "png", "size_bytes": 98321, "data_base64": "..." },
{ "filename": "c.jpg", "success": false, "error": "Invalid or unsupported image" }
]
}Each data_base64 decodes to PNG/WebP bytes with a transparent background.
Same as /remove-batch but takes a JSON array of remote URLs.
Body — JSON:
{
"urls": ["https://example.com/a.jpg", "https://example.com/b.jpg"],
"format": "png"
}Response — same JSON shape as /remove-batch, with url in place of filename.
Returns {"status":"ok","model":"ZhengPeng7/BiRefNet"}. No auth required.
Interactive OpenAPI (Swagger) UI.
| Code | Meaning |
|---|---|
400 |
Invalid image, missing field, malformed URL, invalid hex color, or batch > 10 items |
401 |
Missing Authorization header |
403 |
Invalid bearer token |
413 |
Image exceeds 25 MB limit |
500 |
Server error (check dashboard logs) |
All endpoints apply closed-form foreground matting (via pymatting) after mask prediction. This estimates pure foreground color at soft edges, eliminating color spill from the original background. Result: no halos, no fringing, even on backgrounds that differ sharply from the subject.
import requests
URL = "https://useknockout--api.modal.run/remove"
TOKEN = "kno_public_beta_4d7e9f1a3c5b2e8d6a9f7c1b3e5d8a2f" # public beta token
with open("input.jpg", "rb") as f:
resp = requests.post(
URL,
headers={"Authorization": f"Bearer {TOKEN}"},
files={"file": f},
)
resp.raise_for_status()
with open("output.png", "wb") as f:
f.write(resp.content)npm i @useknockout/nodeimport { writeFile } from "node:fs/promises";
import { Knockout } from "@useknockout/node";
const client = new Knockout({ token: process.env.KNOCKOUT_TOKEN! });
// 1. Remove background → transparent PNG
const png = await client.remove({ file: "./input.jpg" });
await writeFile("out.png", png);
// 2. Replace background with a color
const jpg = await client.replaceBackground({
file: "./input.jpg",
bgColor: "#FF5733",
format: "jpg",
});
await writeFile("out.jpg", jpg);
// 3. Replace background with a remote image
const composed = await client.replaceBackground({
file: "./input.jpg",
bgUrl: "https://example.com/mountains.jpg",
});
// 4. Batch — process 10 URLs in one call
const batch = await client.removeBatchUrl({
urls: ["https://example.com/a.jpg", "https://example.com/b.jpg"],
});
for (const r of batch.results) {
if (r.success) await writeFile(`out-${r.url}.png`, Buffer.from(r.data_base64!, "base64"));
}import { readFile, writeFile } from "node:fs/promises";
const URL = "https://useknockout--api.modal.run/remove";
const TOKEN = process.env.KNOCKOUT_TOKEN;
const buf = await readFile("input.jpg");
const form = new FormData();
form.set("file", new Blob([buf]), "input.jpg");
const res = await fetch(URL, {
method: "POST",
headers: { Authorization: `Bearer ${TOKEN}` },
body: form,
});
if (!res.ok) throw new Error(await res.text());
await writeFile("output.png", Buffer.from(await res.arrayBuffer()));export async function removeBackground(file: File, token: string) {
const form = new FormData();
form.append("file", file);
const res = await fetch("https://useknockout--api.modal.run/remove", {
method: "POST",
headers: { Authorization: `Bearer ${token}` },
body: form,
});
if (!res.ok) throw new Error(`knockout error: ${res.status}`);
return await res.blob(); // PNG with alpha
}package main
import (
"bytes"
"io"
"mime/multipart"
"net/http"
"os"
)
func removeBG(path, token string) ([]byte, error) {
f, err := os.Open(path)
if err != nil { return nil, err }
defer f.Close()
body := &bytes.Buffer{}
w := multipart.NewWriter(body)
part, _ := w.CreateFormFile("file", path)
io.Copy(part, f)
w.Close()
req, _ := http.NewRequest("POST",
"https://useknockout--api.modal.run/remove", body)
req.Header.Set("Authorization", "Bearer "+token)
req.Header.Set("Content-Type", w.FormDataContentType())
resp, err := http.DefaultClient.Do(req)
if err != nil { return nil, err }
defer resp.Body.Close()
return io.ReadAll(resp.Body)
}curl -X POST "https://useknockout--api.modal.run/remove?format=webp" \
-H "Authorization: Bearer $TOKEN" \
-F "file=@input.jpg" \
-o output.webpMeasured on Modal gpu="L4", Python 3.11, torch 2.4, batch size 1, 1024×1024 model input.
| Image size | Warm latency (p50) | Cold start | Output format |
|---|---|---|---|
| 512×512 | 180 ms | ~25 s | PNG / WebP |
| 1024×1024 | 220 ms | ~25 s | PNG / WebP |
| 2048×2048 | 310 ms | ~25 s | PNG / WebP |
| 4000×4000 | 520 ms | ~25 s | PNG / WebP |
BiRefNet (the model we serve) consistently ranks first or second on public benchmarks:
- DIS5K (Dichotomous Image Segmentation): #1 F-measure as of 2024
- HRSOD (High-Resolution Salient Object Detection): #1 MAE
- COD10K (Camouflaged Object Detection): #1 or #2 depending on metric
See the BiRefNet paper and leaderboards for details.
Want to run your own instance? One command after Modal setup.
pip install modal
modal token newgit clone https://github.com/useknockout/api.git
cd api
# create your bearer-token secret
modal secret create knockout-secrets API_TOKEN=$(openssl rand -hex 32)
# deploy
modal deploy main.pyModal prints your live HTTPS URL. First deploy takes ~5 min (image build + weight bake). Subsequent deploys take seconds.
Edit main.py:
@app.cls(
gpu="L4", # or "A10", "A100", "H100"
scaledown_window=60, # seconds of idle before scale-to-zero
max_containers=10, # max concurrent containers
)- Latency-critical? Keep one warm:
min_containers=1(costs ~$0.80/hr 24/7). - Throughput-critical? Bump
max_containersand use@modal.concurrent(max_inputs=4)to batch. - Higher quality? Change
MODEL_INPUT_SIZEto(2048, 2048)— 4x slower, sharper edges.
┌────────────┐ HTTPS ┌───────────────────────────┐
│ Client │ ───────────────▶ │ Modal ASGI (FastAPI) │
│ (any lang) │ │ ┌─────────────────────┐ │
└────────────┘ │ │ Auth (bearer) │ │
│ │ Validation │ │
│ │ Image decode (PIL) │ │
│ │ BiRefNet on L4 GPU │ │
│ │ Encode (PNG/WebP) │ │
│ └─────────────────────┘ │
│ Scale-to-zero, auto-HTTPS │
└───────────────────────────┘
- One file (
main.py), single Modal class, two endpoints + health + docs - Weights baked into image at build time — cold starts are just image pull + GPU model load (~25 s)
- FastAPI handles multipart, JSON, CORS, OpenAPI schema generation
The hosted API at useknockout--api.modal.run is in closed beta while we validate quality. Request an API key: contact.
When the paid tier goes live:
| Tier | Price | Best for |
|---|---|---|
| Free | 50 images / month, no card | Personal, eval, open source |
| Pay-as-you-go | $0.005 / image | Side projects, early startups |
| Volume | $0.003 / image at 100k+/mo | Production workloads |
| Enterprise | Custom, private endpoints | Compliance, BYO-cloud |
For reference — the same image on remove.bg is $0.20 at their PAYG rate.
Credits never expire. No subscriptions. You only pay for what you use.
- GitHub Issues: https://github.com/useknockout/api/issues
- Twitter / X: @useknockout
MIT License — see LICENSE. Model weights (BiRefNet) are also MIT. Commercial use is allowed for both.
Built in a few hours because someone said it couldn't be done.