Remove Background
Remove the background from an image or a video, at a fixed price per task.
Remove Background (product remove_background) removes the background from an image or a video: an image comes back as a transparent PNG, a video as an mp4 on a black background. One task makes one result, at a fixed price per task.
Create a task
https://api.nanoapi.ai/v1/tasksUse the shared create endpoint with model: "remove-background". It usually responds in under 1 second, and the price is frozen within a few seconds. Get the result once the task has finished.
Headers
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer <API key>, see Authentication. |
Content-Type | Yes | application/json |
Idempotency-Key | No | A unique string for every new task, ideally a UUID (1–255 characters). See Preventing duplicate tasks. |
Body
| Parameter | Type | Required | Description |
|---|---|---|---|
model | string | Yes | remove-background. |
image_url | string | One of the two | The image to remove the background from. See Media. |
video_url | string | One of the two | The video to remove the background from, at most 180 seconds. See Media. |
webhook_url | string | No | Notified when the task succeeds or fails: https, up to 2048 characters, not a private, loopback or reserved address. See Webhooks. |
Send exactly one of image_url and video_url: both or neither returns 400 invalid_request, see Errors.
An image:
curl https://api.nanoapi.ai/v1/tasks \
-H "Authorization: Bearer $NANOAPI_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 3c9e7a1f-5b2d-4e8a-a6c1-9f0d2b7e4a63" \
-d '{
"model": "remove-background",
"image_url": "https://example.com/product.jpg"
}'For a video, send video_url instead of image_url.
Response: 200 OK. data is the newly created task, with only these fields; each one is described in Create a task: Response. Keep data.id to get the result. The task can still fail after it's created (for example if your balance can't cover it), so go by what retrieving the task or the webhook tells you.
{
"code": 200,
"message": "success",
"data": {
"object": "task",
"id": "tk_01jbm3r8d5f2h9k6n4p1s7v0x3",
"model": "remove-background",
"status": "queued",
"error": null,
"created_at": "2026-11-03T08:30:00Z"
}
}Get the result
Measured from creation to the end (slower when the generation service is busy): an image takes about 30 seconds; a video depends on its length, about 40 seconds for 10 seconds and 2 minutes for 30 seconds. Once the task has finished (succeeded or failed), get the result either way:
- Webhook (recommended in production): if you passed
webhook_url, the full task is pushed to it once when the task finishes. See Webhooks. - Polling: call
GET /v1/tasks/{task_id}withdata.idat most once every 15 seconds untilstatusissucceededorfailed. Check several tasks at once withidsin List tasks.
The image request above, once it has succeeded (other fields left out):
{
"status": "succeeded",
"input": {
"image_url": "https://example.com/product.jpg"
},
"num_outputs": 1,
"outputs": [
{
"index": 0,
"type": "image",
"url": "https://cdn.nanoapi.ai/h/tk_01jbm3r8d5f2h9k6n4p1s7v0x3_1_1793694600.png",
"preview_url": "https://cdn.nanoapi.ai/h/tk_01jbm3r8d5f2h9k6n4p1s7v0x3_1_1793694600_compressed.jpg"
}
],
"billing": {
"currency": "USD",
"frozen": "…", // the price per task
"charged": "…", // the same
"refunded": "0.00",
"billed_seconds": null
}
}| Field | Remove Background |
|---|---|
outputs | One item. |
outputs[0].type | image when you sent image_url, video when you sent video_url. |
outputs[0].url | The original result, for downloading. Image: a PNG with a transparent background, the same size as the original. Video: an mp4 on a black background, with no alpha channel and no audio track. |
outputs[0].preview_url | For display. Image: a JPEG at most 1024 pixels on the long side, with the transparency shown as white. Video: a compressed h264 mp4, at most 600 pixels wide. null if it couldn't be made. |
billing.billed_seconds | Always null: billed per task. |
input | The parameters you sent. |
On failure the frozen amount is returned in full and the reason is in data.error.type, see Errors. Every field is described in The task object.
Media
The image or video is passed as a public http(s) URL. There's no upload endpoint: host the files yourself. URLs are at most 4096 characters, must download directly and can't point to localhost or private, loopback or reserved addresses.
Image (image_url) | Video (video_url) | |
|---|---|---|
| Length | – | At most 180 seconds (limits.max_source_video_seconds in GET /v1/products) |
| If there's a problem | Read only when generation runs, after the price was frozen: if it can't be downloaded, isn't an image or can't be opened, the task fails with invalid_media and the frozen amount is returned in full. | Likewise invalid_media. A video over 180 seconds fails with video_too_long when the task is submitted to the generation service; the frozen amount is returned in full. |
Keep the link reachable until the task finishes, including links that expire.
Pricing
cost = price per taskLoading the current prices…
- The same for an image and a video, however long the video is.
- It's frozen within a few seconds of creating the task and charged when the task succeeds; when the task fails, all of it is returned. How freezing and charging work in general: Billing.
Read the current price from GET /v1/products (the remove_background product has one prices row, billing_unit task). Prices change, so don't hard-code them; billing.frozen on the retrieved task is what counts.
Errors
Errors and failure reasons specific to this model. Every error of the create endpoint: Create a task → Errors; every failure reason: Failure reasons.
Create request errors: the request fails with an HTTP error and no task is created. param names the parameter.
| Status | error.type | When | What to do |
|---|---|---|---|
400 | invalid_request | Both image_url and video_url were sent (Provide either image_url or video_url, not both, param "video_url"), or neither (image_url or video_url is required, param "image_url"). | Send exactly one of them. |
400 | invalid_request | The link isn't a public http(s) URL of at most 4096 characters, or num_outputs isn't 1. | Fix the parameter param names. |
400 | invalid_request | A parameter this model doesn't take, such as prompt (Extra inputs are not permitted). | Remove it. |
Other errors of the create endpoint (401, 402, 429 and so on): Create a task → Errors.
Task failures: the create request succeeded (200) and the task later turned failed. There's no HTTP status: the reason is the task's error.type, from retrieving the task or the webhook.
error.type | When | What to do |
|---|---|---|
insufficient_balance | Your available balance can't cover the price per task. Within a few seconds of creating the task, nothing frozen. | Top up, then create the task again. |
spend_limit_exceeded | This task's cost would go over one of the key's spend limits. Within a few seconds of creating the task, nothing frozen. | Raise the limit in Console → API keys, or wait for it to reset, then create the task again. |
video_too_long | The video is longer than 180 seconds (The video duration must be at most 180 seconds.). Found when the task is submitted to the generation service, after the price was frozen: all of it is returned. | Trim the video to 3 minutes or less and create the task again. |
invalid_media | The image or video can't be downloaded, isn't an image or a video, or can't be opened (for an image: The image could not be downloaded. Make sure image_url is publicly downloadable. or The image could not be read. It may be corrupted or in an unsupported format.). Found when generation runs, after the price was frozen: all of it is returned. | Replace the file error.message points to and create the task again. |
content_policy | The image or video breaks the content policy. | Resubmitting it as is fails again: use a different file. |
generation_failed | Generation failed. | Create it again later. |
queue_timeout / internal_error | Not started within 30 minutes / an error on our side. | Create it again later. |
Every failed task has its frozen amount returned in full and billing.charged "0.00". Branch on error.type: the quoted messages are for display and may change, see Messages.