Most background-removal APIs make you choose a general-purpose image-editing model and coax it into cutting out a subject with the right prompt. recraft/remove-background skips that step: it's a purpose-built model that takes one image URL and returns a transparent PNG, with hair and fur-level edge detail preserved. No prompt, no negative prompt, no strength parameter — just the source image in, the cutout out.
This guide walks through a complete, tested call: the request shape, the response you'll actually get back, and the production patterns (callbacks, idempotency, error handling) that keep a batch job from stalling on a single bad edge case.
Prerequisites:
- A hiapi API key from your dashboard
- Python 3.9+ with
requestsinstalled (pip install requests), or justcurl - Your source image at a public URL — hiapi's task API fetches images by URL, not file upload. PNG, JPG, or WEBP, up to 5MB, up to 16 megapixels, with both dimensions between 256 and 4096 pixels.
The request
Like every generation model on hiapi, recraft/remove-background runs through the shared async task API: POST /v1/tasks with a model and an input, and you get a taskId back immediately while the cutout runs in the background. The full contract — headers, idempotency keys, status codes — is documented once on Create a task; the model-specific part is input, and for this model it's a single field:
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "recraft/remove-background",
"input": {
"image_url": "https://static.hiapi.ai/model-covers/recraft-remove-background/2026/09/01/golden-input-2048.jpg"
}
}'
import requests
payload = {
"model": "recraft/remove-background",
"input": {
"image_url": "https://static.hiapi.ai/model-covers/recraft-remove-background/2026/09/01/golden-input-2048.jpg",
},
}
response = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers={
"Authorization": "Bearer YOUR_API_KEY",
"Content-Type": "application/json",
},
json=payload,
)
print(response.json())
# {"code": 200, "data": {"taskId": "tk-hiapi-..."}, "message": "success"}
image_url is the only field this model accepts — no format, response_format, or size parameter. Send anything else and the API rejects the whole request with a schema error before it touches the model (see Errors below). Output is always a transparent PNG.
Polling for the result
Take the taskId from the response above and poll GET /v1/tasks/{taskId} until data.status reaches a terminal value. The status enum is the same across every model on the platform: queued → handling → archiving → success (or fail). A real run for this model typically clears in under 10 seconds:
import time
task_id = response.json()["data"]["taskId"]
while True:
r = requests.get(
f"https://api.hiapi.ai/v1/tasks/{task_id}",
headers={"Authorization": "Bearer YOUR_API_KEY"},
)
data = r.json()["data"]
if data["status"] in ("success", "fail"):
break
time.sleep(2)
print(data)
On success, data.output is an array with one entry:
{
"status": "success",
"output": [
{
"artifactId": "119426",
"type": "image",
"url": "https://temp.hiapi.ai/7c6ttvrbpt/01M1RYTHQ5F94S2405YSC65EWM-0.png",
"expireAt": 1789222580
}
]
}
url is a temporary link — expireAt is a Unix timestamp roughly a week out, not a permanent CDN address. Download the bytes and put them in your own storage as soon as the task succeeds:
image_bytes = requests.get(data["output"][0]["url"]).content
with open("cutout.png", "wb") as f:
f.write(image_bytes)
Production patterns
Use a callback instead of polling in a loop. Pass callback.url at creation time and hiapi POSTs the final result to you when the task finishes, instead of you burning requests on a poll loop:
payload = {
"model": "recraft/remove-background",
"callback": {"url": "https://example.com/hiapi/callback", "when": "final"},
"input": {"image_url": "https://your-cdn.example.com/product-01.jpg"},
}
when: "final" means you get exactly one callback, on the terminal status (success or fail), not one per intermediate state. This is the right default for a batch of product photos where you don't want to hold open a poll loop per image.
Idempotency. If a request times out on your end but actually reached hiapi, retrying with the same Idempotency-Key header (documented on Create a task) returns the original task instead of billing a second cutout. Worth setting on any batch job that retries on network errors.
Errors — two shapes, not one. A malformed request and an authentication failure come back in different envelopes, and code that only checks one will silently swallow the other:
// Bad input (schema validation) — HTTP 400
{"code": 400, "data": null, "error_code": "INVALID_REQUEST", "message": "invalid input: image_url: missing required field \"image_url\""}
// Bad or expired API key — HTTP 401
{"error": {"code": "permission_denied", "message": "This API key is invalid...", "request_id": "...", "type": "hiapi_error"}}
Check for error_code first, then fall back to error.code — don't assume every failure has the same top-level keys.
Pricing. $0.01 per image (20 credits), flat regardless of input resolution. Current numbers are always on the pricing page.
When you want a replacement background, not just a cutout
recraft/remove-background only produces a transparent cutout — no fill, no scene. If the goal is swapping in a studio backdrop or a lifestyle scene in the same call, that's a different job: an image-to-image model prompted to regenerate the background around a preserved subject. See Remove Image Backgrounds with an API: Prompt-Based Editing for that approach and when it's worth the extra cost over a plain cutout.
FAQ
Does recraft/remove-background accept a prompt or strength parameter?
No. The schema is strict — image_url is the only accepted field. Sending extras like format or response_format returns a 400 with an "additional properties not allowed" message.
What image formats and sizes are supported? Input: PNG, JPG, or WEBP, up to 5MB, up to 16 megapixels, both dimensions between 256 and 4096 pixels. Output is always PNG with an alpha channel.
Can I upload a local file instead of a URL?
No — image_url must be a public http(s):// URL that hiapi's servers can fetch. Upload to object storage or a CDN first.
How long is the output URL valid?
The expireAt field on each output item is a Unix timestamp, typically about a week out. Treat it as temporary and copy the bytes to your own storage right after the task succeeds — don't store the temp.hiapi.ai link long-term.
Does this work for batch processing hundreds of product photos?
Yes — submit one task per image with a shared callback.url, and let hiapi push results back instead of polling each one. At $0.01/image, a 1,000-image catalog run costs $10 in generation.









