Search

Search HiAPI models, tools, and resources.

Back to blog
Tutorial

How to use 851-labs/background-remover via the hiapi API: curl, Python, and a working request

hiapi851-labsBackground RemovalImage APITutorial

851-labs/background-remover turns a product, portrait, or pet photo into a transparent PNG with a single API call — no prompt, no mask, no aspect-ratio settings. This recipe covers the exact request shape, a working curl command, a Python script you can drop into a backend, and the one schema mistake that trips people up.

What you need

  • An hiapi account and an API key from the dashboard.
  • One image hosted at a public HTTPS URL. The task API takes URLs, not file uploads — if your source image only exists locally, put it on any CDN or object storage first.

All requests go to the unified task endpoint:

POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json

The request shape (and the one rule that matters)

The model id is 851-labs/background-remover, passed bare. Its input schema has exactly one field:

FieldTypeRequiredNotes
image_urlstringyesMust match ^https?://[^\s]+$ — a directly reachable http(s) URL.

That's the entire schema. Two things worth knowing before you write any code:

  1. There is no prompt, mask, background-color, or aspect-ratio field. This endpoint does one job — foreground/background separation — and nothing else. If you need to recolor or resize afterward, that's a second call to a different model.
  2. Extra fields are rejected, not ignored. Carry over a format or size field from another model's payload and you get 400 INVALID_REQUEST: <root>: additional properties 'format' not allowed before the task even starts.

Minimal working example: curl

Create the task:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "851-labs/background-remover",
    "input": {
      "image_url": "https://your-cdn.example.com/product-shot.jpg"
    }
  }'

A successful submission returns a task id inside data:

{"code": 200, "data": {"taskId": "tk-hiapi-..."}}

Poll for the result:

curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
  -H "Authorization: Bearer sk-<your-key>"

While running, data.status moves through queued → handling → archiving. On completion it flips to "success" and the transparent PNG appears in data.output:

{
  "code": 200,
  "data": {
    "status": "success",
    "output": [{"url": "https://.../result.png", "expireAt": "..."}]
  }
}

The output URL is short-lived (note the expireAt). Download the bytes as soon as the task succeeds — don't hotlink or persist the raw URL.

Production-ready Python: submit, poll, download

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def remove_background(image_url: str) -> bytes:
    # 1. Create the task
    resp = requests.post(API_BASE, headers=HEADERS, json={
        "model": "851-labs/background-remover",
        "input": {"image_url": image_url},
    }, timeout=60)
    body = resp.json()
    if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
        raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
    task_id = body["data"]["taskId"]

    # 2. Poll until terminal state
    deadline = time.time() + 300
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
                            timeout=30).json().get("data") or {}
        if task.get("status") == "success":
            outputs = task.get("output") or []
            if not outputs or not outputs[0].get("url"):
                raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
            # 3. Download immediately — the URL expires
            png = requests.get(outputs[0]["url"], timeout=120)
            png.raise_for_status()
            return png.content
        if task.get("status") == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(3)
    raise TimeoutError(f"task {task_id} still running after 300s")


if __name__ == "__main__":
    png = remove_background("https://your-cdn.example.com/product-shot.jpg")
    with open("cutout.png", "wb") as f:
        f.write(png)
    print(f"saved cutout.png ({len(png)} bytes)")

Background removal is a light image task, so it typically finishes well inside the 300-second deadline above — the timeout is a safety net, not the expected runtime.

Callbacks instead of polling

For a web backend, register a callback at task-creation time instead of polling in a request handler. The callback object sits at the top level, next to model:

{
  "model": "851-labs/background-remover",
  "input": { "image_url": "https://your-cdn.example.com/product-shot.jpg" },
  "callback": {
    "url": "https://your-app.example.com/hooks/hiapi",
    "when": "final"
  }
}

callback.when only supports "final" — you get exactly one POST when the task reaches success or fail, not progress events. Key your handler on taskId so a redelivered webhook doesn't double-process, and keep a periodic sweep over GET /v1/tasks/<id> for any id your endpoint never confirmed.

Error handling: the errors that matter

400 INVALID_REQUEST at creation — the message names the exact field:

MessageCause
invalid input: image_url: missing required field "image_url"input was empty or the key was misspelled
image_url: 'not-a-url' does not match pattern '^https?://[^\s]+$'The value isn't a well-formed http(s) URL
<root>: additional properties 'format' not allowedYou sent a field this model doesn't accept

These are permanent — retrying the same payload fails forever. Fix the field and resend.

401 permission_denied at creation — the key itself is the problem, not the request body:

{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}

Check the key's model permissions in the dashboard.

status: "fail" on the task — creation succeeded but the run itself failed (usually the backend couldn't fetch image_url). Read data.error.code / message; this class of failure is safe to retry once with backoff.

A GET for an unknown or expired task id returns 404 with {"code": 404, "data": null, "message": "task not found"} — treat that as terminal in any reconciliation sweep, not as "still pending."

Where to go next

FAQ

Do I need a prompt? No. Supply one image_url and the model separates the foreground from the background on its own.

Is the output actually transparent? Yes — the result is a PNG with an alpha channel, ready to composite onto any background.

Can I change the aspect ratio, colors, or background in the same call? No. This endpoint only removes the background. For recoloring, resizing, or replacing the background, chain a second call to an image-editing model using the transparent PNG as input.

Why do I get "additional properties '...' not allowed"? The schema takes exactly one field, image_url. Any field carried over from another model's payload — format, size, background_color, anything — is rejected outright rather than silently ignored.

How should I save the result? Download it as soon as the task reaches status: "success". The output URL is signed and carries an expireAt; it isn't meant for long-term hotlinking.

What does it cost? Pricing is usage-based per image. Check the current rate on the pricing page.

Generate it with HiAPI

Choose a model, enter your prompt, and see the result.

HiAPI Blog

Related articles

HiAPI

Generate it with HiAPI

Start generating