Tutorial de la API de Seedream 5.5: generación, edición y migración

Genera y edita imágenes con la API de Seedream 5.5: ID del modelo, estado de la tarea, descargas autenticadas, sondeo asíncrono y dificultades de migración.

AIHubMix10 min de lectura
Tutorial de la API de Seedream 5.5: generación, edición y migración

La migración útil a Seedream 5.5 consiste en un flujo de archivos, no solo en sustituir el nombre del modelo: genera una imagen, inspecciona la tarea devuelta, guarda la imagen y luego envía esos bytes guardados como referencia para una edición.

Seedream 5.5 en AIHubMix usa actualmente doubao-seedream-5-5 y POST /ai/v1/images/generations. Este tutorial crea una imagen de producto de una taza de cerámica y luego solicita una edición que solo cambia el color.

Alcance: documentación consultada el 12 de noviembre de 2026. El código solo se sometió a comprobaciones locales de sintaxis y de funciones auxiliares sin conexión; no se ejecutó ninguna solicitud de generación. Estas comprobaciones no demuestran la calidad de los resultados, la fidelidad de la edición, la latencia ni el costo real.

1. Prepara una solicitud breve y explícita

Usa Python 3.10 o posterior con requests. En Bash, instala la dependencia e introduce la clave sin guardar su valor en el historial del shell:

python3 -m pip install requests
read -r -s -p 'AIHubMix API key: ' AIHUBMIX_API_KEY
printf '\n'
export AIHUBMIX_API_KEY

Para el código implementado, inyecta la variable de entorno mediante tu administrador de secretos. No incluyas la clave en archivos de origen, ejemplos de solicitudes, registros, notebooks ni código del lado del navegador. Antes de cualquiera de las dos solicitudes POST, activa la función de tareas de la cuenta como se indica en la guía de tareas asíncronas. Este requisito previo se aplica a los endpoints nativos de tareas multimedia /ai/v1, incluido el modo de espera síncrona; es independiente de la elección de la ejecución en segundo plano.

Consulta el esquema actual del endpoint del modelo y selecciona la entrada por path, no por la posición en el arreglo. Las restricciones específicas del modelo tienen prioridad sobre una lista de parámetros compartida.

Configuración Valor utilizado aquí
Modelo doubao-seedream-5-5
Ruta POST /ai/v1/images/generations
Cuerpo JSON plano
Tamaño 2048x2048
Cantidad de imágenes n = 1
Formato de archivo png
Formato de respuesta url

El esquema requiere model y un prompt no vacío, limita n a 1 y admite imágenes de salida en png y jpeg. Acepta una URL en image, una entrada Base64 o un objeto {url}; images acepta un arreglo con un máximo publicado de 14 referencias. Ese límite no se ha probado en tiempo de ejecución aquí. Este ejemplo usa una referencia y especifica el tamaño predeterminado; no incluye un objeto externo input ni extensiones extra.

2. Genera, inspecciona la tarea y guarda el PNG

La documentación de la API unificada de imágenes define una respuesta de tarea con id, object: image, model, status, output y error. No la interpretes como el formato heredado data[0].url.

Primero, revisa el flujo breve; la configuración completa y la función auxiliar de descarga están en el Apéndice desplegable que aparece más abajo. Antes de ejecutar estos ejemplos, carga una vez ese bloque del apéndice en la misma sesión de Python. Define settings, api_json y collect sin enviar ninguna solicitud de imagen. Luego ejecuta los bloques breves en orden. Cada POST es una acción de pago independiente; no la vuelvas a ejecutar para corregir una respuesta ausente o una descarga fallida.

Envía la solicitud de imagen una sola vez

generation = {
    **settings,
    "prompt": "Product photograph of one matte blue ceramic mug on a cream tabletop, "
              "soft side lighting, plain warm-gray background, no text or logos.",
}
task = api_json("POST", IMAGES + "/generations", generation)

Inspecciona la tarea y luego guarda el PNG

Omitir async significa esperar de forma síncrona. Una respuesta HTTP 200 aún puede contener status: failed. La función auxiliar guarda el ID de la tarea antes de inspeccionar el estado: solo si está completed continúa con la descarga; los estados failed, cancelled y los desconocidos detienen el proceso. Los estados pending y in_progress requieren inspeccionar la tarea existente, no iniciar otra generación.

collect(task, "mug-blue")

Esta única llamada usa la clave con la que se creó la tarea para recuperar el resultado completado, comprueba los bytes del PNG y guarda mug-blue.png. No reintenta automáticamente la creación y, antes de reenviar la clave, rechaza hosts de descarga o redirecciones inesperados. La implementación completa está en el apéndice; este comportamiento se basa en la documentación y no en un resultado de generación verificado.

La guía del modelo indica que las URL de salida duran aproximadamente 30 minutos. Descarga el archivo cuanto antes con el mismo encabezado Bearer y guarda los bytes por tu cuenta. expires_at: null no promete una conservación permanente. Si b64_json contiene datos, decodifícalos en lugar de descargar una URL.

3. Edita el archivo local, no su URL de descarga protegida

Inspecciona tú mismo la imagen guardada antes de pagar una edición. Que tu cliente autenticado pueda descargar una content_url no significa necesariamente que sea una URL pública accesible para el proveedor de imágenes. Vuelve a cargar el PNG local como URI de datos:

reference = Path("mug-blue.png").read_bytes()
if not reference.startswith(b"\x89PNG\r\n\x1a\n"):
    raise SystemExit("The editing reference must be the saved PNG.")
edit = {
    **settings,
    "prompt": "Change only the mug color from blue to forest green. Preserve the mug shape, "
              "camera angle, tabletop, background, lighting, and shadows.",
    "image": "data:image/png;base64," + base64.b64encode(reference).decode("ascii"),
}
task = api_json("POST", IMAGES + "/generations", edit)
collect(task, "mug-green")

Esto solicita que se conserve la imagen, pero no demuestra que se haya conservado. Revisa el resultado antes de iniciar otra ronda de pago. Toda la solicitud JSON, incluida la codificación insertada, debe caber en 32 MiB; también se aplican los límites multimedia específicos del modelo. Otra opción de entrada válida es una URL de imagen realmente pública y accesible para el proveedor.

4. Opcional: espera asíncrona y recuperación tras una interrupción

El requisito previo de la cuenta indicado en la sección 1 es independiente del modo de ejecución. 403 async_not_enabled significa que hay que activar la función de tareas en la consola. Solo la ejecución en segundo plano es opcional.

Si prefieres la generación en segundo plano, crea el mismo diccionario generation y luego usa las dos líneas de abajo en lugar de la línea síncrona task = api_json(...) y la llamada independiente collect(...). Elige el modo antes de enviar la solicitud; no ejecutes ambas versiones:

task = api_json("POST", IMAGES + "/generations", {**generation, "async": True})
collect(task, "mug-blue", poll=True)

La función auxiliar consulta GET /ai/v1/images/{id} aproximadamente cada 15 segundos hasta alcanzar un estado terminal. Es la frecuencia de sondeo recomendada en la documentación, no un intervalo impuesto por el servidor. Los webhooks requieren async: true; la configuración de un receptor queda fuera del alcance de este tutorial.

Si se detuvo la recopilación del resultado, vuelve a cargar únicamente el bloque compartido de configuración y funciones auxiliares del apéndice en una sesión nueva, si hace falta; no vuelvas a cargar un bloque de creación. Si guardaste un ID, reanuda sin hacer ningún POST:

task_id = Path("mug-blue.task-id").read_text(encoding="utf-8").strip()
task = api_json("GET", IMAGES + "/" + quote(task_id, safe=""))
collect(task, "mug-blue", poll=True)

Usa el nombre base correspondiente, mug-green, para recuperar la edición. Si la respuesta de creación desapareció antes de que se guardara un ID, inspecciona las tareas recientes:

recent = api_json("GET", IMAGES + "?limit=20&order=desc")
for row in recent.get("data", []):
    print(row.get("id"), row.get("model"), row.get("status"), row.get("created_at"))

Comprueba que coincidan el modelo y la hora de creación; no selecciones a ciegas la tarea más reciente. Si hace falta, usa next_after como cursor after de la siguiente solicitud. Si no puedes identificar la tarea con certeza, contacta con soporte en lugar de volver a enviar la solicitud. Una lista es una instantánea; consulta los detalles de la tarea para sondear su estado. output_error en una fila significa que no se pudo leer el resultado, no que la generación no haya producido nada ni que haya cambiado el cobro.

Sigue usando la clave con la que se creó la tarea. En particular, el acceso unificado a listas, detalles y contenido de tareas está aislado por token: otra clave de la misma cuenta no puede leer esas tareas. Una interrupción local no demuestra que la tarea haya fallado ni que no se haya cobrado.

5. Errores de migración y errores actuales

La documentación del adaptador heredado de Doubao enumera ID anteriores bajo /v1/models/doubao/<id>/predictions con un contenedor input. Su inventario no incluye la versión 5.5. Usa aquí la ruta 5.5 disponible mediante la documentación y el cuerpo plano; no se probó ningún fallo de generación con la ruta heredada. Copia exactamente el ID actual con guiones y no des por hecho que una variante con puntos sea un alias.

No conserves los parámetros de nivel superior quality, background, aspect_ratio ni seed: no aparecen en el esquema actual de este modelo. El anuncio de lanzamiento de ByteDance describe una salida RGBA transparente nativa, pero el esquema de la pasarela no expone ni background ni layer_decomposition. Seleccionar PNG por sí solo no es una receta de transparencia verificada.

Estos son errores de la API unificada de imágenes documentados, no respuestas observadas de la versión 5.5. Comprueba por separado el estado HTTP y el estado de la tarea, y decide según code, no según el texto completo del mensaje.

Dónde Código Siguiente acción
HTTP 400 schema_violation Corrige los campos del esquema
HTTP 400 capability_not_supported Elimina las opciones no admitidas
HTTP 504 sync_timeout Recupera la tarea; contacta con soporte
HTTP 502 task_status_unavailable No vuelvas a enviar la solicitud
HTTP 422 output_blocked Modifica el contenido
HTTP 403 async_not_enabled Activa la función de tareas
Tarea fallida result_delivery_failed Investiga el resultado existente

Para HTTP 502 task_status_unavailable, la guía indica:

No se pudo confirmar el estado de la tarea en el proveedor del modelo. No vuelvas a enviarla. Contacta con soporte e incluye el ID de la tarea o el ID de la solicitud.

Para el error de tarea result_delivery_failed, indica:

No se pudo entregar el resultado generado. No vuelvas a enviar la solicitud automáticamente. Contacta con soporte e incluye el ID de la tarea.

Un sync_timeout no demuestra que la ejecución nunca se haya producido. Conserva error.tid, usa un ID de tarea solo si realmente se devolvió uno y pide a soporte que resuelva los resultados inciertos. Si se puede recuperar de un fallo de descarga, vuelve a intentar obtener el archivo, no la generación. 410 artifact_expired es distinto: el artefacto ya no se puede recuperar. Primero, comprueba los archivos guardados; si aún necesitas un reemplazo, autoriza deliberadamente una nueva generación de pago. Ante 429 too_many_downloads, comprueba si tienes copias guardadas o contacta con soporte; esperar no garantiza que se renueve el acceso.

La guía exime explícitamente del cargo de generación por output_blocked, incluso cuando se informa en una tarea fallida. No extiendas esa excepción a todas las solicitudes fallidas o moderadas; output_policy_violation se rige por las reglas de facturación de moderación vigentes.

6. Presupuesta todo el flujo, no solo un resultado

El informe de lanzamiento de Sina Technology indica un costo aproximado de CNY 0.28 por una edición cuadrada de 2K, combinando la imagen de entrada y la de salida, con los tokens de texto gratuitos. Es información de prensa, no una tarifa oficial verificada ni un precio de AIHubMix.

A modo de ejemplo, dos rondas de edición de pago de una imagen existente, cada una acorde con ese escenario aproximado, cuestan alrededor de 2 × CNY 0.28 = CNY 0.56. Esto no incluye la generación inicial de texto a imagen, imágenes de referencia adicionales, candidatos adicionales ni reintentos. No es un importe de factura medido.

Para este ejemplo de generación seguida de edición, usa total = cargo por generación inicial + cargo por edición con referencia. Las páginas consultadas no permitieron establecer una tarifa oficial completa para la versión 5.5, así que este tutorial no da un total fijo ni una proporción precisa del ahorro frente a la versión anterior.

Antes de enviar la solicitud:

  • Confirma el ID actual, la ruta, el cuerpo plano y el esquema del modelo.
  • Mantén la clave en el entorno y guarda los ID de las tareas para poder recuperarlas.
  • Trata cada POST como una acción de pago independiente, nunca como un reintento de descarga.
  • Comprueba el estado de la tarea y la imagen guardada antes de editar.
  • Vuelve a subir los bytes locales con el tipo MIME correcto.
  • Recupera las tareas de resultado incierto antes de plantearte generar otra imagen.

Empieza con una imagen y una edición deliberada; añade más rondas solo después de revisar los archivos.

Apéndice: configuración compartida y función auxiliar de descarga

Omite este apéndice al seguir el flujo de trabajo. Antes de ejecutar los ejemplos breves, despliega y carga este bloque una vez en la misma sesión de Python. Configura la sesión y define funciones sin generar una imagen. Para recuperar una tarea interrumpida en una sesión nueva, vuelve a cargar solo este bloque, no uno de creación o edición.

Despliega la función auxiliar completa: cárgala una vez antes de los ejemplos breves; no realiza solicitudes de imagen
import base64
import os
import time
from pathlib import Path
from urllib.parse import quote, urlsplit

import requests

BASE = "https://aihubmix.com"
IMAGES = "/ai/v1/images"
settings = {
    "model": "doubao-seedream-5-5",
    "size": "2048x2048",
    "n": 1,
    "output_format": "png",
    "response_format": "url",
}
client = requests.Session()
client.headers["Authorization"] = "Bearer " + os.environ["AIHUBMIX_API_KEY"]


def api_json(method, path, body=None):
    try:
        response = client.request(
            method, BASE + path, json=body,
            timeout=(15, 600), allow_redirects=False,
        )
    except requests.RequestException:
        raise SystemExit(
            "Connection interrupted. Recover the existing task before another POST."
        ) from None
    try:
        data = response.json()
    except ValueError:
        raise SystemExit(
            f"HTTP {response.status_code}: non-JSON response; recover before another POST."
        ) from None
    if not isinstance(data, dict):
        raise SystemExit("Unexpected response; recover before another POST.")
    if not 200 <= response.status_code < 300:
        error = data.get("error") or {}
        raise SystemExit(
            f"HTTP {response.status_code}: code={error.get('code')}, "
            f"tid={error.get('tid')}. No automatic generation retry."
        )
    return data


def collect(task, stem, poll=False):
    task_id = task.get("id")
    if not isinstance(task_id, str) or not task_id:
        raise SystemExit("No task ID returned. Recover from the task list; do not resubmit.")
    Path(stem + ".task-id").write_text(task_id + "\n", encoding="utf-8")
    while True:
        status = task.get("status")
        print("Task:", task_id, "status:", status)
        if status == "completed":
            break
        if status not in {"pending", "in_progress"}:
            error = task.get("error") or {}
            raise SystemExit(
                f"Stopped: status={status}, code={error.get('code')}; task={task_id}."
            )
        if not poll:
            raise SystemExit("Task still active. Query the saved ID; do not recreate it.")
        time.sleep(15)
        task = api_json("GET", IMAGES + "/" + quote(task_id, safe=""))

    output = task.get("output") or []
    if not output:
        raise SystemExit("Completed without readable output; investigate the saved task.")
    item = output[0]
    if item.get("b64_json"):
        image_bytes = base64.b64decode(item["b64_json"], validate=True)
    else:
        url = item.get("content_url")
        if not isinstance(url, str):
            raise SystemExit("Missing content_url; investigate the saved task.")
        parsed = urlsplit(url)
        if (parsed.scheme, parsed.netloc) != ("https", "aihubmix.com"):
            raise SystemExit("Unexpected download origin; key was not forwarded.")
        try:
            response = client.get(url, timeout=(15, 120), allow_redirects=False)
        except requests.RequestException:
            raise SystemExit("Download interrupted; recover this task, not a new generation.") from None
        if not 200 <= response.status_code < 300:
            try:
                detail = response.json()
            except ValueError:
                detail = {}
            error = detail.get("error", {}) if isinstance(detail, dict) else {}
            code = error.get("code") if isinstance(error, dict) else None
            if response.status_code == 410 or code == "artifact_expired":
                raise SystemExit("Artifact expired; retrieval cannot recover it. Check saved files first.")
            if code == "too_many_downloads":
                raise SystemExit("Download allowance exhausted; check saved files or contact support.")
            raise SystemExit(
                f"Download HTTP {response.status_code}, code={code}; "
                "inspect the saved task. Retry retrieval only if recoverable."
            )
        image_bytes = response.content
    if not image_bytes.startswith(b"\x89PNG\r\n\x1a\n"):
        raise SystemExit("Expected PNG bytes; stopped before saving or reusing the result.")
    destination = Path(stem + ".png")
    destination.write_bytes(image_bytes)
    print("Saved:", destination)
    return destination

El tiempo de espera de lectura de 600 segundos es una elección del cliente, no una promesa sobre la latencia del servicio. La comprobación de PNG coincide con el formato de salida explícito de este tutorial; adáptala si eliges JPEG.

Preguntas frecuentes

¿Qué ID del modelo y endpoint debe usar Seedream 5.5?
Usa doubao-seedream-5-5 con POST /ai/v1/images/generations y JSON plano. No des por sentado que otra forma de escribirlo o un adaptador heredado sean equivalentes.

¿Este tutorial incluye resultados de generación probados?
No. El código se basa en la documentación y solo se comprobaron localmente la sintaxis y las funciones auxiliares sin conexión. No se ejecutó ninguna solicitud de inferencia ni prueba de calidad de imagen, latencia o facturación.

¿Es obligatorio usar async?
No; la espera síncrona es la opción predeterminada. La ejecución en segundo plano utiliza el valor booleano async: true. Es necesario que la cuenta tenga activada la función de tareas para usar los endpoints nativos de tareas multimedia, incluso si se espera de forma síncrona.

¿HTTP 200 significa que la imagen se generó correctamente?
No. Comprueba el estado de la tarea y sus errores. Una tarea puede fallar aunque la solicitud HTTP tenga éxito; descarga la imagen solo cuando esté completada.

¿Se puede usar como referencia para la siguiente edición la URL de descarga del resultado?
No des por sentado que sí. Descárgalo con la clave que lo generó y luego codifica la imagen local como un URI de datos, o proporciona una URL de imagen realmente pública.

¿Qué hacer tras un tiempo de espera agotado o una respuesta perdida?
No repitas la generación automáticamente. Comprueba el ID de tarea guardado o recupéralo de la lista de imágenes con la clave que la creó. Conserva los ID de solicitud y ponte en contacto con soporte si el resultado es incierto.

¿Elegir PNG garantiza una salida transparente o un cobro de CNY 0.28?
Ninguna de las dos cosas. El esquema no ofrece un control de transparencia verificado. CNY 0.28 es un ejemplo aproximado de coste de edición en 2K mencionado en la prensa, no una tarifa de AIHubMix ni un precio fijo para la generación de texto a imagen más la edición.

Sigue leyendo: la serie Seedream 5.5

Fuentes

  1. Anuncio del lanzamiento de Seedream 5.5 — ByteDance Seed; afirmaciones del proveedor sobre sus capacidades.
  2. Esquema actual del endpoint de Seedream 5.5 — AIHubMix; campos de solicitud y restricciones específicas del modelo.
  3. API unificada de generación de imágenes — AIHubMix; tareas, descargas y errores actuales.
  4. Guía del modelo Seedream 5.5 — AIHubMix; ID del modelo y descargas autenticadas de corta duración; consulta el documento de la API unificada para ver los errores específicos de cada ruta.
  5. Noticias sobre el lanzamiento de Seedream 5.5 — Sina Technology; escenario de coste de edición aproximado, no una tarifa oficial.

Un solo gateway para varios proveedores de IA. Diseñado para producción.

Visitar AIHubMix