Tutoriel sur l’API Seedream 5.5 : génération, retouches et migration

Générez et retouchez des images avec l’API Seedream 5.5 : ID du modèle, état des tâches, téléchargements authentifiés, interrogation asynchrone et pièges de migration.

AIHubMix10 min de lecture
Tutoriel sur l’API Seedream 5.5 : génération, retouches et migration

Pour une migration réussie vers Seedream 5.5, il faut mettre en place un flux de traitement des fichiers, et pas seulement remplacer le nom du modèle : générez une image, examinez la tâche renvoyée, enregistrez l’image, puis envoyez les octets du fichier enregistré comme référence pour une retouche.

Seedream 5.5 sur AIHubMix utilise actuellement doubao-seedream-5-5 et POST /ai/v1/images/generations. Ce tutoriel crée un visuel de produit représentant une tasse en céramique, puis demande une retouche portant uniquement sur la couleur.

Périmètre : documentation vérifiée le 12 novembre 2026. Le code a uniquement fait l’objet de vérifications syntaxiques locales et de tests hors ligne des fonctions auxiliaires ; aucune requête de génération n’a été exécutée. Ces vérifications ne prouvent ni la qualité du résultat, ni la fidélité de la retouche, ni la latence, ni le montant réellement facturé.

1. Préparer une requête simple et explicite

Utilisez Python 3.10+ avec requests. Dans Bash, installez la dépendance et saisissez la clé sans que sa valeur soit enregistrée dans l’historique du shell :

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

En production, injectez la variable d’environnement via votre gestionnaire de secrets. Ne placez pas la clé dans les fichiers source, les exemples de requêtes, les journaux, les notebooks ni le code côté navigateur. Avant l’un ou l’autre POST, activez la fonctionnalité de tâches du compte, comme indiqué dans le guide des tâches asynchrones. Ce prérequis s’applique aux endpoints natifs de tâches multimédias /ai/v1, y compris en cas d’attente synchrone ; il est distinct du choix d’une exécution en arrière-plan.

Consultez le schéma actuel de l’endpoint du modèle et sélectionnez l’entrée à l’aide de path, et non de sa position dans le tableau. Les contraintes propres au modèle prévalent sur une liste de paramètres partagée.

Paramètre Valeur utilisée ici
Modèle doubao-seedream-5-5
Route POST /ai/v1/images/generations
Corps JSON à plat
Taille 2048x2048
Nombre d’images n = 1
Format de fichier png
Format de réponse url

Le schéma exige model et un prompt non vide, limite n à 1 et indique que les formats de sortie pris en charge sont png et jpeg. Il accepte une URL image, une entrée Base64 ou un objet {url} ; images accepte un tableau comportant au maximum 14 références selon la documentation. Cette limite n’a pas été testée à l’exécution ici. Cet exemple utilise une seule référence et indique explicitement la taille par défaut, sans objet input englobant ni extensions extra.

2. Générer, examiner la tâche et enregistrer le PNG

La documentation de l’API unifiée d’images définit une réponse de tâche comprenant id, object: image, model, status, output et error. Ne la lisez pas comme l’ancien format data[0].url.

Commencez par lire le flux de traitement résumé ; le code complet de configuration et de téléchargement se trouve dans l’Annexe dépliable ci-dessous. Avant d’exécuter ces exemples, chargez une fois le bloc de cette annexe dans la même session Python. Il définit settings, api_json et collect sans envoyer de requête d’image. Exécutez ensuite les blocs courts dans l’ordre. Chaque POST est une action payante distincte ; ne le relancez pas pour remédier à une réponse manquante ou à un échec de téléchargement.

Envoyez la requête d’image une seule fois

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)

Examinez la tâche, puis enregistrez le PNG

Omettre async signifie que l’attente est synchrone. Une réponse HTTP 200 peut tout de même contenir status: failed. La fonction auxiliaire enregistre l’ID de la tâche avant d’examiner son état : seul completed permet de passer au téléchargement ; les états failed, cancelled et ceux qui sont inconnus interrompent le processus. Les états pending et in_progress nécessitent l’examen d’une tâche existante, et non une nouvelle génération.

collect(task, "mug-blue")

Cet appel unique utilise la clé ayant servi à créer la tâche pour récupérer le résultat final, vérifie que les octets correspondent à un PNG et enregistre le fichier sous mug-blue.png. Il ne relance pas automatiquement la création et refuse les hôtes de téléchargement ou redirections inattendus avant de transmettre la clé. L’implémentation complète se trouve dans l’annexe ; il s’agit du comportement décrit dans la documentation, et non d’un résultat de génération vérifié.

Le guide du modèle indique que les URL de sortie restent valides environ 30 minutes. Téléchargez rapidement le fichier avec le même en-tête Bearer et stockez vous-même les octets. expires_at: null ne garantit pas une conservation permanente. Si b64_json est renseigné, décodez-le au lieu de télécharger une URL.

3. Retoucher le fichier local, pas l’URL de téléchargement protégée

Examinez vous-même l’image enregistrée avant de payer pour une retouche. Un content_url téléchargeable par votre client authentifié n’est pas nécessairement une URL publique accessible au fournisseur d’images. Importez à nouveau le PNG local sous forme de Data URI :

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")

Cette demande vise à préserver l’image ; elle ne démontre pas que cette préservation a eu lieu. Examinez le résultat avant de lancer une autre opération payante. La requête JSON complète, encodage intégré compris, doit tenir dans 32 MiB ; les limites multimédias propres au modèle s’appliquent également. Une URL d’image réellement publique et accessible au fournisseur constitue une autre option d’entrée acceptée.

4. Facultatif : attente asynchrone et reprise après interruption

Le prérequis lié au compte indiqué à la section 1 est distinct du mode d’exécution. 403 async_not_enabled signifie que la fonctionnalité de tâches doit être activée dans la console. Seule l’exécution en arrière-plan est facultative.

Si vous préférez la génération en arrière-plan, créez le même dictionnaire generation, puis utilisez les deux lignes ci-dessous à la place de la ligne synchrone task = api_json(...) et de l’appel collect(...) séparé. Choisissez le mode avant l’envoi ; n’exécutez pas les deux versions :

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

La fonction auxiliaire interroge GET /ai/v1/images/{id} environ toutes les 15 secondes jusqu’à atteindre un état terminal. Il s’agit de la fréquence d’interrogation recommandée dans la documentation, et non d’un intervalle imposé par le serveur. Les webhooks nécessitent async: true ; leur configuration de réception sort du cadre de ce tutoriel.

Si la récupération s’est interrompue, rechargez si nécessaire uniquement le bloc de configuration et de fonction auxiliaire partagée de l’annexe dans une nouvelle session, pas un bloc de création. Si un ID a été enregistré, reprenez sans aucun 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)

Utilisez le préfixe correspondant mug-green pour récupérer la retouche. Si la réponse de création a disparu avant l’enregistrement d’un ID, examinez plutôt les tâches récentes :

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"))

Faites correspondre le modèle et l’heure de création ; ne sélectionnez pas aveuglément la tâche la plus récente. Si nécessaire, utilisez next_after comme curseur after de la requête suivante. Si la correspondance est ambiguë, contactez le support plutôt que de soumettre une nouvelle requête. Une liste est un instantané ; utilisez les détails de la tâche pour l’interroger. output_error sur une ligne signifie que le résultat n’a pas pu être lu, et non que la génération n’a rien produit ou que la facturation a changé.

Continuez à utiliser la clé ayant servi à créer la tâche. En particulier, l’accès unifié à la liste des tâches, à leurs détails et à leur contenu est isolé par token : une autre clé du même compte ne peut pas lire ces tâches. Une interruption locale ne prouve ni un échec ni l’absence de frais.

5. Erreurs de migration et erreurs actuelles

La documentation de l’adaptateur Doubao historique répertorie d’anciens ID sous /v1/models/doubao/<id>/predictions, avec une enveloppe input. Son inventaire ne mentionne pas la version 5.5. Utilisez ici la route 5.5 répertoriée et un corps à plat ; aucun échec de génération via l’ancien endpoint n’a été testé. Reprenez exactement l’ID actuel avec des traits d’union au lieu de supposer qu’une version avec des points est un alias.

Ne reprenez pas les paramètres de premier niveau quality, background, aspect_ratio ou seed : ils ne figurent pas dans le schéma actuel de ce modèle. L’annonce de lancement de ByteDance décrit une sortie RGBA transparente native, mais le schéma de la passerelle n’expose ni background ni layer_decomposition. Choisir PNG ne constitue pas une méthode de transparence vérifiée.

Il s’agit d’erreurs documentées de l’API unifiée d’images, et non de réponses observées pour Seedream 5.5. Vérifiez séparément le statut HTTP et l’état de la tâche, puis orientez le traitement selon code, pas selon le texte intégral du message.

Contexte Code Action suivante
HTTP 400 schema_violation Corriger les champs du schéma
HTTP 400 capability_not_supported Supprimer les options non prises en charge
HTTP 504 sync_timeout Reprendre ; contacter le support
HTTP 502 task_status_unavailable Ne pas soumettre à nouveau
HTTP 422 output_blocked Modifier le contenu
HTTP 403 async_not_enabled Activer la fonctionnalité de tâches
Tâche échouée result_delivery_failed Examiner le résultat existant

Pour HTTP 502 task_status_unavailable, le guide indique :

Impossible de confirmer l’état de la tâche chez le fournisseur du modèle. Ne la soumettez pas à nouveau. Contactez le support en indiquant l’ID de la tâche ou l’ID de la requête.

Pour l’erreur au niveau de la tâche result_delivery_failed, il indique :

Impossible de transmettre le résultat généré. Ne soumettez pas automatiquement une nouvelle requête. Contactez le support en indiquant l’ID de la tâche.

sync_timeout ne permet pas de conclure que l’exécution n’a jamais eu lieu. Conservez error.tid, n’utilisez un ID de tâche que s’il a réellement été renvoyé et demandez au support de clarifier les résultats incertains. En cas d’échec de téléchargement récupérable, réessayez de récupérer le fichier plutôt que de relancer la génération. 410 artifact_expired est différent : il n’est plus possible de récupérer cet artefact. Vérifiez d’abord les fichiers enregistrés ; si vous avez toujours besoin d’un remplacement, autorisez délibérément une nouvelle génération payante. En cas de 429 too_many_downloads, vérifiez les copies enregistrées ou contactez le support ; attendre ne garantit pas le rétablissement de l’accès.

Le guide indique explicitement que les frais de génération sont annulés pour output_blocked, y compris lorsque cette erreur est signalée dans une tâche échouée. N’étendez pas cette exception à toutes les requêtes ayant échoué ou ayant été modérées ; output_policy_violation reste soumis aux règles de facturation existantes en matière de modération.

6. Budgéter le flux de traitement, pas seulement un résultat

L’article de lancement de Sina Technology rapporte un coût d’environ 0,28 CNY pour la retouche d’une image carrée 2K, en combinant l’image d’entrée et de sortie, les tokens de texte étant gratuits. Il s’agit d’une information relayée par la presse, et non d’une grille tarifaire officielle vérifiée ou d’un tarif AIHubMix.

Exemple : deux retouches payantes d’une image existante, chacune correspondant à ce scénario arrondi, coûtent environ 2 × CNY 0.28 = CNY 0.56. Cela exclut la génération initiale à partir de texte, les images de référence supplémentaires, les autres propositions et les nouvelles tentatives. Il ne s’agit pas d’une facture mesurée.

Pour cet exemple de génération suivie d’une retouche, utilisez total = frais de génération initiale + frais de retouche de référence. Les pages examinées ne permettent pas d’établir une grille tarifaire officielle complète pour la version 5.5 ; ce tutoriel ne donne donc ni total fixe ni ratio précis d’économies entre l’ancienne et la nouvelle version.

Avant l’envoi :

  • Vérifiez l’ID actuel, la route, le corps à plat et le schéma du modèle.
  • Conservez la clé dans l’environnement et les ID de tâche pour la reprise.
  • Considérez chaque POST comme une action payante distincte, et non comme une nouvelle tentative de téléchargement.
  • Vérifiez l’état de la tâche et l’image enregistrée avant de procéder à la modification.
  • Téléversez à nouveau les données locales avec le type MIME correct.
  • Récupérez les tâches dont l’issue est incertaine avant d’envisager une nouvelle génération.

Commencez par une image et une modification réfléchie ; n’ajoutez d’autres étapes qu’après avoir examiné les fichiers.

Annexe : configuration partagée et fonction de téléchargement

Ignorez cette annexe si vous suivez le workflow. Avant d’exécuter les exemples courts, développez et chargez une fois ce bloc dans la même session Python. Il configure la session et définit des fonctions sans générer d’image. Pour récupérer une tâche interrompue dans une nouvelle session, rechargez uniquement ce bloc, et non un bloc de création ou de modification.

Développez l’intégralité de l’aide — à charger une fois avant les exemples courts ; aucune requête d’image
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

Le délai d’attente de lecture de 600 secondes est un choix côté client, et non une garantie de latence du service. La vérification PNG correspond au format de sortie explicitement utilisé dans ce tutoriel ; adaptez-la si vous choisissez JPEG.

FAQ

Quel ID de modèle et quel endpoint utiliser avec Seedream 5.5 ?
Utilisez doubao-seedream-5-5 avec POST /ai/v1/images/generations et du JSON plat. Ne partez pas du principe qu’une autre orthographe ou un adaptateur hérité est équivalent.

Ce tutoriel présente-t-il des résultats de génération testés ?
Non. Le code s’appuie sur la documentation ; seules la syntaxe locale et les fonctions d’aide hors ligne ont été vérifiées. Aucune requête d’inférence, mesure de qualité d’image, mesure de latence ni vérification de facturation n’a été effectuée.

Le mode asynchrone est-il obligatoire ?
Non ; l’attente synchrone est le comportement par défaut. L’exécution en arrière-plan utilise le booléen async: true. L’activation de la fonctionnalité de tâches au niveau du compte est une condition préalable pour les endpoints natifs de tâches média, même en cas d’attente synchrone.

Un HTTP 200 signifie-t-il que l’image a bien été générée ?
Non. Vérifiez l’état de la tâche et son erreur éventuelle. Une tâche peut échouer même si la requête HTTP réussit ; ne téléchargez l’image qu’une fois la tâche terminée.

Une URL de téléchargement du résultat peut-elle servir de référence pour la modification suivante ?
Ne le présumez pas. Téléchargez le résultat avec la clé qui a servi à le créer, puis encodez l’image locale en Data URI ou fournissez une URL d’image réellement publique.

Que faire après un délai d’attente dépassé ou une réponse perdue ?
Ne relancez pas automatiquement la génération. Vérifiez l’ID de tâche enregistré ou récupérez la tâche dans la liste des images avec la clé utilisée pour la création. Conservez les ID de requête et contactez l’assistance si l’issue reste incertaine.

Le choix du PNG garantit-il une sortie transparente ou une facture de CNY 0.28 ?
Ni l’un ni l’autre. Le schéma ne prévoit aucun contrôle de transparence vérifié. CNY 0.28 correspond à un exemple approximatif de coût de modification en 2K rapporté par la presse, et non à un tarif AIHubMix ni à un prix fixe pour la génération de texte en image suivie d’une modification.

Poursuivez votre lecture : la série Seedream 5.5

Sources

  1. Annonce de lancement de Seedream 5.5 — ByteDance Seed ; caractéristiques annoncées par le fournisseur.
  2. Schéma actuel de l’endpoint Seedream 5.5 — AIHubMix ; champs de requête et contraintes propres au modèle.
  3. API unifiée de génération d’images — AIHubMix ; tâches, téléchargements et erreurs actuelles.
  4. Guide du modèle Seedream 5.5 — AIHubMix ; ID du modèle et téléchargements authentifiés à durée de vie limitée ; pour les erreurs propres à une route, consultez le document sur l’API unifiée.
  5. Couverture du lancement de Seedream 5.5 — Sina Technology ; exemple de coût approximatif de modification, et non tarif officiel.

Une seule passerelle vers plusieurs fournisseurs d’IA. Conçue pour la production.

Découvrir AIHubMix