Tutorial
Cómo llamar a seedream-4.5/image-to-image a través de la API de HiAPI: curl y Python
Una solicitud ejecutable de principio a fin, que cubre la entrada de múltiples imágenes de referencia, el sondeo asíncrono y el procesamiento de la salida
Qué hace este endpoint y cuándo elegirlo
seedream-4.5/image-to-image convierte una o varias imágenes de referencia junto con un prompt de texto en una nueva imagen. Pertenece a la familia de imagen a imagen de HiAPI: tú proporcionas el contexto visual y el modelo lo reinterpreta según tus instrucciones. Esta es precisamente la diferencia clave con respecto a texto a imagen, donde el prompt es la única entrada y el modelo crea todo desde cero.
Elige imagen a imagen cuando quieras que el resultado visual siga anclado a material existente: rediseñar el estilo de un recurso existente, cambiar la iluminación o el fondo manteniendo la forma y la disposición del producto, iterar la composición sin perder el sujeto, o garantizar la coherencia visual de un lote de resultados reutilizando las mismas imágenes de referencia.
Elige texto a imagen cuando no tengas material de referencia y solo quieras describir una escena con palabras.
Este endpoint es asíncrono. Envías una tarea, recibes un identificador y luego sondeas hasta que la tarea alcanza un estado terminal, momento en el que obtienes la salida. Planifica este ciclo de vida desde el principio; el resto de esta guía construye una solicitud ejecutable y luego la refuerza.
Construir una solicitud curl ejecutable
Todas las solicitudes a este endpoint comparten la misma estructura básica. Revisa cada campo uno por uno.
Endpoint y método
La solicitud se envía con POST a la ruta de imagen a imagen bajo la URL base de HiAPI, usando el identificador de modelo seedream-4.5/image-to-image. El nombre del modelo es un valor dentro del payload de la solicitud, no parte de la ruta.
Autenticación
Pasa tu clave de API de HiAPI como bearer token en la cabecera Authorization. Guárdala en una variable de entorno para que no acabe en el historial del shell ni en el control de versiones:
export HIAPI_API_KEY="your-key-here"
Payload mínimo de la solicitud
curl -sS -X POST "$HIAPI_BASE_URL/v1/images/generations" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedream-4.5/image-to-image",
"prompt": "restyle this photo as a warm studio portrait",
"image": "https://example.com/reference.jpg"
}'
Campo por campo:
model: el identificador literalseedream-4.5/image-to-image.prompt: qué hacer con la imagen de referencia. Describe la transformación, no solo el sujeto.image: la entrada de referencia. Para una sola imagen, es un único valor; la siguiente sección cubre varias imágenes de referencia.
Leer la primera respuesta
La llamada devuelve una tarea aceptada, no una imagen terminada. Captura el identificador de la tarea del cuerpo de la respuesta y consérvalo; lo necesitarás al sondear. No interpretes un estado de tipo 200 como "la imagen está lista".
Antes de escribir cualquier código de cliente, ejecuta esta solicitud tal cual una vez. Un payload que falla en curl fallará en Python por las mismas razones, y depurar en el shell es más rápido.
Portar la solicitud a Python
La versión en Python mantiene el payload byte a byte idéntico, sustituyendo solo la capa de transporte y el manejo de la autenticación. Esta separación es la clave: si curl funciona, Python debería funcionar al primer intento.
import os
import requests
BASE_URL = os.environ["HIAPI_BASE_URL"]
API_KEY = os.environ["HIAPI_API_KEY"]
payload = {
"model": "seedream-4.5/image-to-image",
"prompt": "restyle this photo as a warm studio portrait",
"image": "https://example.com/reference.jpg",
}
headers = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
resp = requests.post(
f"{BASE_URL}/v1/images/generations",
json=payload,
headers=headers,
timeout=30,
)
resp.raise_for_status()
job = resp.json()
print(job["id"])
Estas indicaciones te ahorrarán tiempo más adelante:
- Construye el payload como un dict de Python y deja que
json=se encargue de la serialización. Escribir cadenas JSON a mano es la fuente típica de errores de comillas. - Mantén la construcción de la autenticación en una función auxiliar. Si cambia el formato de la cabecera, solo modificas un lugar.
- Pasa siempre un
timeoutexplícito también en la llamada de envío. Un cuelgue de socket en el paso de envío es indistinguible de un modelo lento. - Guarda de inmediato el identificador de la tarea devuelto, en lugar de volver a deducirlo a partir de los logs.
El código de sondeo de las secciones siguientes usa los mismos BASE_URL, API_KEY y headers, así que mantenlos en el ámbito del módulo o en una pequeña clase de cliente.
Pasar varias imágenes de referencia
La entrada de múltiples imágenes de referencia es donde más fallan las solicitudes de imagen a imagen, normalmente porque el modelo recibe tres imágenes y el prompt solo describe una de ellas.
Organizar las entradas de forma explícita
Coloca las imágenes de referencia como una lista dentro del mismo campo image, en el orden que esperas, y haz que el prompt indique el papel de cada posición:
payload = {
"model": "seedream-4.5/image-to-image",
"prompt": (
"Use the first reference for the subject's pose, "
"the second for the background setting, "
"and the third for color grading. Keep the subject sharp."
),
"image": [
"https://example.com/pose.jpg",
"https://example.com/background.jpg",
"https://example.com/grading.jpg",
],
}
Por qué etiquetar es mejor que la posición
El orden de la lista es una convención, pero el prompt es lo que le dice al modelo cómo interpretar ese orden. Un array vago con un prompt ambiguo obliga al modelo a adivinar qué imagen de referencia tiene prioridad cuando entran en conflicto. Indicar el papel de cada imagen en el prompt convierte suposiciones implícitas sobre la posición en instrucciones explícitas.
Reglas prácticas:
- Mantén un número reducido de imágenes de referencia, cada una con un propósito claro. Las imágenes de referencia sobrantes diluyen el resultado en lugar de enriquecerlo.
- Indica cómo resolver los conflictos: si la imagen de referencia de la pose y la del fondo no coinciden en el encuadre, especifica cuál tiene prioridad.
- Usa URL estables y de acceso público, o un mecanismo de carga equivalente. Una imagen de referencia que devuelve 404 a mitad de la tarea hace fallar toda la solicitud.
Si la salida ignora alguna imagen de referencia, la solución habitual no es volver a llamar al modelo, sino indicar el papel de esa imagen con una frase más clara en el prompt.
Gestionar el ciclo de vida de la tarea asíncrona
La generación de imágenes lleva tiempo, por eso este endpoint se basa en tareas. Trata el ciclo de vida como cuatro pasos independientes y escribe el código de cada uno.
1. Envío
Envía el POST mostrado anteriormente. Antes de hacer cualquier otra cosa, persiste el identificador de la tarea de la respuesta.
2. Sondeo
Con las mismas cabeceras de autenticación, haz una solicitud GET a la ruta de la tarea hasta que el estado alcance un estado terminal:
import time
def poll_job(job_id, interval=2.0, max_interval=30.0, deadline=300.0):
start = time.monotonic()
while True:
r = requests.get(
f"{BASE_URL}/v1/images/generations/{job_id}",
headers=headers,
timeout=30,
)
r.raise_for_status()
data = r.json()
status = data["status"]
if status in ("succeeded", "failed"):
return data
if time.monotonic() - start > deadline:
raise TimeoutError(f"job {job_id} not finished within {deadline}s")
time.sleep(interval)
interval = min(interval * 2, max_interval)
3. Detectar el estado terminal
Solo dos resultados terminan el bucle: éxito o fallo. Cualquier otro caso significa seguir esperando. Nunca asumas que la salida es instantánea y nunca hagas un número fijo de sondeos sin un límite de tiempo real.
4. Obtener el resultado
Se trata en la siguiente sección.
El backoff importa. Un bucle apretado con sleep(0.1) gasta tu presupuesto de tasa en comprobaciones de estado. Empezar con unos dos segundos y duplicar hasta un límite superior mantiene la latencia baja sin saturar la API.
Recuperar y almacenar la salida
Cuando el sondeo devuelve un estado de éxito, la respuesta incluye una referencia a la salida. Lo que se devuelve es un puntero a la imagen generada, no necesariamente los bytes en sí.
Descargar de forma deliberada
data = poll_job(job_id)
url = data["output"][0]["url"] if isinstance(data["output"], list) else data["output"]
with requests.get(url, stream=True, timeout=60) as r:
r.raise_for_status()
with open(f"{job_id}.png", "wb") as f:
for chunk in r.iter_content(chunk_size=8192):
f.write(chunk)
Ajusta la ruta según la estructura de respuesta real que observes en tu primera tarea real, en lugar de suponer un esquema fijo.
Reglas de almacenamiento para evitar retrabajos
- Nombra los archivos con el identificador de la tarea. Es único, rastreable y vincula el artefacto con la solicitud que lo produjo.
- Descarga con prontitud. Los enlaces de salida pueden tener caducidad, así que persiste la salida justo después de un sondeo exitoso, en lugar de guardar la URL en caché para usarla más tarde.
- Procesa los archivos grandes en streaming, en lugar de leerlos completos en memoria.
- Registra el prompt y la lista de imágenes de referencia junto con el archivo. Cuando la salida sea inesperada, las entradas son el registro de depuración.
Si necesitas reintentos deterministas, guardar el identificador de la tarea significa que puedes volver a sondear o volver a descargar sin pagar por una segunda generación.
Errores, reintentos y control de costes
La mayoría de los fallos pertenecen a una de tres categorías, y cada una requiere un tratamiento distinto.
Errores de cliente
Payload mal formado, falta de model, URL de imagen de referencia inaccesible o clave incorrecta. Estos errores se devuelven de inmediato y se seguirán devolviendo al reintentar. Corrige la solicitud; no reintentes en bucle. Verifica que la URL de la imagen de referencia se resuelve antes del envío.
Errores temporales de servidor y de red
Tiempos de espera agotados, conexiones reiniciadas, respuestas de límite de tasa. Merece la pena reintentarlos con backoff exponencial y jitter, con un número máximo de intentos bajo. Reintenta el paso concreto (envío, sondeo o descarga) en lugar de todo el pipeline. Reenviar después de un envío exitoso es precisamente cómo se paga dos veces sin querer.
Fallos a nivel de tarea
La tarea alcanza el estado terminal de fallo. Reintentar con el mismo identificador de tarea no ayuda. Decide explícitamente si reenviar con un prompt o un conjunto de imágenes de referencia modificados, y registra el motivo del fallo para poder distinguir los errores de entrada de las fallas temporales.
def with_retries(fn, attempts=4, base=1.0):
for i in range(attempts):
try:
return fn()
except requests.RequestException:
if i == attempts - 1:
raise
time.sleep(base * (2 ** i))
Control de costes
La facturación está ligada a la generación, así que el error caro es el reenvío. Impón un límite de tiempo real al sondeo, mantén el envío idempotente guardando el identificador de la tarea antes de sondear, y limita la concurrencia para que una tormenta de reintentos no se convierta en decenas de tareas facturadas. Registra cada envío con su identificador de tarea; ese log es tu rastro de auditoría del gasto.
Conecta estos tres aspectos en el cliente de una vez y el mismo código manejará solicitudes de una sola imagen de referencia y de varias sin cambios.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado