HiAPI
Blog

Tutorial

Eliminar el fondo de una imagen con la API: edición basada en prompts

Cómo usar un modelo de imagen a imagen en hiapi para reemplazar o eliminar fondos de imágenes: el mínimo código, el modo por lotes y los detalles del schema que mantienen la composición de las fotos de producto perfectamente exacta.

Aug 6, 2026
Eliminar el fondo de una imagen con la API: edición basada en prompts

Las herramientas tradicionales de eliminación de fondos ofrecen una sola operación: recortar el sujeto y dejar un área transparente (o un relleno de color sólido). Con la eliminación de fondos basada en prompts mediante un modelo de imagen a imagen, obtienes en una sola llamada tanto el recorte como el fondo reemplazado —un fondo de estudio blanco sin costuras, una escena de estilo de vida, un color de marca sólido— porque el modelo regenera el fondo píxel a píxel alrededor del sujeto preservado, en lugar de limitarse a calcular una máscara alfa.

Esta guía mostrará exactamente cómo hacerlo en hiapi: qué modelos de imagen a imagen destacan realmente en la eliminación de fondos, la estructura de solicitud/respuesta con la que debes codificar, y los patrones de producción (callbacks, reintentos, manejo de errores) que evitan que las tareas por lotes contaminen silenciosamente tu catálogo de productos.

Requisitos previos:

  • Obtén una clave de API de hiapi desde tu panel
  • Python 3.9+ con requests instalado (pip install requests)
  • Tu imagen de origen alojada en una URL pública: la API de tareas de hiapi obtiene las imágenes por URL, no mediante carga de archivos, por lo que las rutas de archivos locales no sirven. Cualquier almacenamiento de objetos o CDN vale.

Cómo funciona la eliminación de fondos como tarea de imagen a imagen

Cada generación en hiapi —imagen, vídeo o audio— pasa por el mismo ciclo de vida de tarea asíncrona: envías una tarea, obtienes un taskId y luego consultas el resultado periódicamente o lo recibes mediante una URL de callback. El contrato completo está en la referencia de la API de tareas asíncronas; para la eliminación de fondos, lo importante es:

  1. POST /v1/tasks con model + input (tu prompt, la URL de la imagen de origen y la forma de salida) devuelve {"code":200,"data":{"taskId":"tk-hiapi-..."}}. En este punto todavía no hay campo status.
  2. GET /v1/tasks/{taskId} devuelve data.status, con uno de estos valores: queued, handling, archiving, success, fail. Solo success y fail son estados terminales.
  3. Cuando el estado es success, data.output es un array de {"url","type","expireAt"}. url es un enlace firmado de corta duración (expireAt es una marca de tiempo Unix): descárgalo de inmediato, no almacenes ese enlace directo.

En cuanto a los modelos, usa el endpoint de imagen a imagen y haz que el prompt describa el fondo reemplazado, en lugar de decir aisladamente "eliminar el fondo": estos modelos responden mucho mejor a objetivos concretos que a instrucciones negativas:

Remove the background completely and replace it with a seamless, pure white
studio background. Keep the subject's shape, edges, lighting, and colors
exactly unchanged. Do not add shadows, reflections, or any other objects
that were not in the original image.

Ejemplo mínimo: una imagen de entrada, fondo reemplazado

seedream-5.0-lite/image-to-image es la opción más económica y suficiente para este uso: acepta prompt, image_urls, aspect_ratio y resolution (solo admite 2K o 4K; este endpoint no tiene nivel 1K, y campos desconocidos como n o seed se rechazan directamente con un 400).

import os
import time
import requests

API = "https://api.hiapi.ai/v1"
HEADERS = {
    "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
    "Content-Type": "application/json",
}

BACKGROUND_PROMPT = (
    "Remove the background completely and replace it with a seamless, pure "
    "white studio background. Keep the subject's shape, edges, lighting, and "
    "colors exactly unchanged. Do not add shadows, reflections, or any other "
    "objects that were not in the original image."
)

def create_task(model: str, task_input: dict) -> str:
    resp = requests.post(f"{API}/tasks", headers=HEADERS,
                          json={"model": model, "input": task_input}, timeout=60)
    resp.raise_for_status()
    return resp.json()["data"]["taskId"]

def wait_task(task_id: str, timeout_s: int = 180, poll_s: int = 3) -> dict:
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        resp = requests.get(f"{API}/tasks/{task_id}", headers=HEADERS, timeout=30)
        resp.raise_for_status()
        task = resp.json()["data"]
        if task["status"] == "success":
            return task
        if task["status"] == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task {task_id} failed: {err.get('code')} {err.get('message')}")
        time.sleep(poll_s)
    raise TimeoutError(f"task {task_id} still {task['status']} after {timeout_s}s")

task_id = create_task("seedream-5.0-lite/image-to-image", {
    "prompt": BACKGROUND_PROMPT,
    "image_urls": ["https://your-cdn.example.com/product-photo.jpg"],
    "aspect_ratio": "1:1",
    "resolution": "2K",
})
task = wait_task(task_id)
image_bytes = requests.get(task["output"][0]["url"], timeout=60).content

with open("product-no-bg.png", "wb") as f:
    f.write(image_bytes)

Ese es todo el bucle: enviar, consultar, descargar. Para una verificación rápida sin código antes de integrarlo en un pipeline, AI Product Photo Lab ejecuta este mismo tipo de reemplazo de fondo en el navegador.

Procesamiento por lotes de catálogos de productos

Los catálogos de productos reales tienen decenas o cientos de imágenes, y la API de tareas no tiene un endpoint nativo por lotes: debes expandir tú mismo los envíos de tareas individuales y consultarlos de forma concurrente:

from concurrent.futures import ThreadPoolExecutor, as_completed

def remove_background(image_url: str, aspect_ratio: str = "1:1") -> str:
    task_id = create_task("seedream-5.0-lite/image-to-image", {
        "prompt": BACKGROUND_PROMPT,
        "image_urls": [image_url],
        "aspect_ratio": aspect_ratio,
        "resolution": "2K",
    })
    task = wait_task(task_id)
    return task["output"][0]["url"]

source_urls = [
    "https://your-cdn.example.com/sku-1001.jpg",
    "https://your-cdn.example.com/sku-1002.jpg",
    # ...
]

results = {}
with ThreadPoolExecutor(max_workers=5) as pool:
    futures = {pool.submit(remove_background, url): url for url in source_urls}
    for future in as_completed(futures):
        url = futures[future]
        try:
            results[url] = future.result()
        except Exception as exc:
            results[url] = None
            print(f"failed: {url}: {exc}")

Mantén max_workers moderado: consultar con demasiada frecuencia sobre muchas tareas concurrentes solo consume tu cuota de solicitudes y no acelera la generación, porque el tiempo real de cada tarea lo determina principalmente la inferencia del modelo, no tu frecuencia de consulta.

Refuerzo para producción: composición exacta, callbacks, idempotencia, errores

Conserva la composición exacta del producto. En el trabajo de catálogo, normalmente no puedes tolerar que el modelo recomponga o recorte ligeramente el sujeto: un zapato centrado en la entrada debe seguir centrado en la salida. seedream-5.0-pro/image-to-image admite un valor de aspect_ratio que el nivel lite no admite: match_input_image, que fija el lienzo de salida a las dimensiones propias de la imagen de entrada, en lugar de forzar una proporción fija. También acepta hasta 10 image_urls por llamada y limita resolution a 1K/2K (un conjunto de resoluciones distinto al de lite: no asumas que el schema es igual entre endpoints de seedream, cada endpoint tiene sus propias enumeraciones). Para trabajos de eliminación y reemplazo de fondo puros en los que la composición debe coincidir con exactitud, este es el nivel que debes elegir, con un coste mayor por imagen; consulta las tarifas actuales en precios.

Si necesitas hacer referencia a varios ángulos de origen en una sola vez (por ejemplo, la vista frontal y la lateral del mismo producto) en lugar de procesar una imagen por llamada, nano-banana-2 es una alternativa razonable: acepta imágenes de referencia mediante image_input (en lugar de image_urls), y además resolution puede ser 1K/2K/4K.

Usa callbacks en lugar de consultas. Para cualquier tarea por lotes a escala real, el webhook supera a la consulta periódica: añade un objeto callback como hermano de input, no anidado dentro de él; POST /v1/tasks rechaza esa estructura:

task_input = {
    "prompt": BACKGROUND_PROMPT,
    "image_urls": [image_url],
    "aspect_ratio": "1:1",
    "resolution": "2K",
}
resp = requests.post(f"{API}/tasks", headers=HEADERS, json={
    "model": "seedream-5.0-lite/image-to-image",
    "input": task_input,
    "callback": {"url": "https://your-service.example.com/hooks/hiapi", "when": "final"},
}, timeout=60)

Cuando la tarea alcanza un estado terminal, hiapi hace POST a tu URL de callback con el mismo objeto data que devolvería GET /v1/tasks/{taskId}. Si quieres verificar la autenticidad del callback en el servidor, consulta la cabecera HMAC opcional X-HiAPI-Signature en la referencia de tareas asíncronas.

Idempotencia. No hay un campo idempotency_key documentado en POST /v1/tasks: implementa la idempotencia en la capa de aplicación. Usa un pequeño almacén (una fila de base de datos, una clave de Redis o incluso un archivo local para un script de un solo uso) con una clave basada en el hash de (image_url, prompt, model) y, antes de enviar una nueva tarea, comprueba si esa clave ya tiene un taskId o una URL de salida almacenados. Si tu tarea por lotes se cae a mitad y se vuelve a ejecutar, podrás omitir el cobro duplicado de las imágenes ya procesadas en lugar de aumentar los costes en silencio.

Manejo de errores: tres formas distintas, no las trates igual:

  • Validación de la solicitud (entrada incorrecta en la creación): HTTP 400, estructura plana: {"code":400,"error_code":"INVALID_REQUEST","message":"...","data":null}. Significa que tu diccionario input no coincide con el schema del modelo: comprueba si hay nombres de campo mal escritos o valores de enumeración incorrectos.
  • Fallo de autenticación: HTTP 401, anidado de forma distinta: {"error":{"code":"permission_denied","message":"...","request_id":"...","type":"hiapi_error"}}. La clave de API falta o ha caducado, no es un error de la solicitud: no reintentes, corrige la clave.
  • Fallo a nivel de tarea (la solicitud es válida, pero la generación en sí falla): se manifiesta cuando consultas: data.status == "fail", y data.error describe el motivo. Esto es reintentable; un error transitorio del proveedor puede tener éxito si se reenvía.
if task["status"] == "fail":
    err = task.get("error") or {}
    # log err["code"] / err["message"], then decide: retry, skip, or alert

No agrupes los tres casos en un único except Exception: reintentar un 401 en un bucle solo consume tu límite de velocidad y nunca tendrá éxito, mientras que un fail a nivel de tarea sí puede tener éxito en el segundo intento.

Siguientes pasos

Preguntas frecuentes

¿Esto realmente elimina el fondo o solo lo cubre? Funcionalmente es ambas cosas: el modelo regenera todo el lienzo alrededor del sujeto preservado, por lo que no deja máscaras residuales ni bordes irregulares como las herramientas de recorte por detección de bordes. El precio es que debes indicar explícitamente en el prompt "mantén todo lo demás sin cambios"; de lo contrario, el modelo reestilizará encantado también el sujeto.

¿Puedo obtener un PNG transparente en lugar de un fondo de color sólido? Pídelo directamente en el prompt (por ejemplo, "reemplaza el fondo por transparencia" o "aísla el sujeto sobre un fondo transparente") y solicita salida PNG donde el modelo admita el campo output_format: la compatibilidad varía según el modelo, así que consulta la página del modelo concreto antes de darlo por hecho.

¿Por qué mi imagen vuelve recompuesta en lugar de coincidir con la original? Probablemente estés usando un nivel cuya enumeración de aspect_ratio no incluye match_input_image: en los niveles lite/4.5 eliges una proporción fija, que puede recortar o añadir barras negras respecto a la imagen de origen. Cambia a seedream-5.0-pro/image-to-image y establece aspect_ratio en match_input_image.

¿Cuánto cuesta cada imagen? El precio varía según el modelo y el nivel de resolución, y cambia con el tiempo: consulta precios en lugar de codificar cifras fijas en tu propio código o documentación.

¿Qué pasa si mi URL de imagen aún no es pública? La API de tareas obtiene las imágenes por URL en el servidor, por lo que debe ser accesible sin autenticación. Súbela primero a cualquier bucket público o ruta de CDN: una URL firmada/privada o una ruta localhost fallará al crear la tarea o (peor) fallará en silencio durante el procesamiento.

Artículos relacionados

Ver todos los artículos →

Empieza a crear con artefactos de IA persistentes

Consigue tu API key y llama a modelos de imagen, vídeo y audio a través de una única API basada en tasks.

Leer la documentación de la API