Créez une image, validez-la, puis animez-la en un court clip. Ces étapes reprennent notre essai de la tasse bleue d’octobre 2026. Ce guide utilise une image fixe Z-Image à 0,010 $ et cinq secondes de MiniMax H3 en 768p à 0,185 $ : 0,195 $ pour un essai à chaque étape, sans extension du prompt pour l’image fixe. Essayez d’abord la même scène dans le Playground ; une fois les entrées au point, utilisez l’exemple Python ou confiez les appels à un agent.
Le résultat visé est précis : une tasse bien formée sur un établi, de la vapeur dérivant vers la droite et un lent travelling avant qui se termine à l’arrêt. Vous n’avez ni modèle à déployer ni serveur GPU à configurer.
Essayez le cas complet dans le Playground
Créez votre compte et ajoutez du solde, puis vérifiez votre adresse e-mail. Le Playground gère la requête ; une clé API n’est nécessaire que pour le code et les outils d’agent ci-dessous.
1. Générez l’image de départ
Ouvrez Z-Image texte vers image et utilisez son Playground. Collez ce prompt :
Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench in a pottery studio, a thin curl of steam rising from the mug. Soft window light from the left, shallow depth of field, shelves of pots softly out of focus behind. The mug sits left of centre with empty bench to its right. Natural colours, no text, no logos.
Choisissez 16:9, PNG, le seed 42, et désactivez l’extension du prompt. Vérifiez le prix affiché, puis appuyez sur Run model. L’image de base coûte 0,010 $ ; l’extension porterait ce prix à 0,016 $. Gardez le seed 42 fixe lorsque vous comparez les changements du prompt.
Ouvrez le résultat avant de poursuivre. Vérifiez :
- Objet : une tasse, une anse et une géométrie de céramique nette.
- Possibilité de mouvement : une vapeur visible et de l’espace à droite pour sa dérive.
- Composition : la tasse est à gauche du centre ; les étagères restent un fond discret.
- Détails indésirables : aucune inscription parasite, aucun logo ni tasse supplémentaire.
Téléchargez le PNG validé sous image.png. Si la tasse est incorrecte, modifiez une phrase et générez une nouvelle image avant de dépenser pour la vidéo. Chaque nouvelle image de base ajoute 0,010 $.
2. Animez l’image validée
Ouvrez MiniMax H3 image vers vidéo, ajoutez image.png dans Input image et collez dans Prompt :
Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.
Réglez Duration sur 5 secondes et Resolution sur 768p. Vérifiez le prix et appuyez sur Run model. Ce clip coûte 0,185 $ ; le choix de 2k coûterait 0,290 $.
Le prompt sépare mouvement de vapeur, mouvement de caméra et son. Il demande à la tasse de rester en place plutôt que de la décrire de nouveau. Si elle traverse l’établi, simplifiez la consigne de caméra au lieu d’ajouter des actions à l’objet. Un lent travelling avant rapproche la caméra ; il ne demande pas à la tasse de bouger.
3. Vérifiez le clip, puis choisissez la prise suivante
Lisez tout le résultat avec le son. Vérifiez que la tasse ne glisse pas et ne change pas de forme, que la caméra avance puis s’arrête, qu’il n’y a pas de coupe indésirable et que le son convient à un atelier calme. Le livrable attendu est un MP4 d’environ cinq secondes avec une piste audio ; un fichier lisible ne suffit pas à rendre le plan exploitable.
Si l’image était bonne mais le mouvement incorrect, gardez l’image de départ validée et révisez le prompt de mouvement. Si l’image de départ était incorrecte, remplacez-la d’abord. H3 n’a pas de seed : une génération en 2k produit une nouvelle prise, dont les mouvements peuvent différer de la version 768p qui vous plaisait.
Ce que l’essai de la tasse bleue a produit
L’image fixe a coûté 0,010 $ et a été livrée en PNG de 1280 × 720. La requête vidéo de cinq secondes a coûté 0,125 $ (en 480p, qui n’est plus proposé ; le même clip en 768p coûte 0,185 $) et a renvoyé un MP4 de 832 × 480 avec piste audio. Le total était de 0,135 $ ; en 768p, il serait de 0,195 $.
La tasse est restée sur l’établi tandis que la vapeur montait et dérivait à droite ; le travelling était discret. L’image fixe plaçait la tasse légèrement à gauche du centre, et la vapeur devenait moins distincte près de l’étagère vers la fin. Pour un plan produit plus serré, précisez davantage la position de la tasse dans le prompt d’image avant l’animation.
Pourquoi ces deux modèles conviennent au premier essai
Z-Image fournit une image peu coûteuse pour fixer le sujet et la composition. H3 accepte cette image et génère mouvement et bande-son. Leurs entrées diffèrent :
| Z-Image texte vers image | H3 image vers vidéo | |
|---|---|---|
| ID du modèle | alibaba/z-image-turbo/text-to-image | minimax/h3/image-to-video |
| Entrée requise | prompt (1 à 1 000 caractères) | prompt (jusqu’à 7 000) et image_url |
| Réglages utilisés ici | aspect_ratio (1:1, 4:3, 3:4, 16:9 ou 9:16), output_format (uniquement png), seed, enable_prompt_expansion | duration_seconds (4 à 15), resolution (768p ou 2k) |
| Non accepté | tout autre format de sortie | seed et aspect_ratio : l’image de départ définit la forme |
| Sortie | un PNG | un MP4 avec bande-son générée |
| Prix | 0,010 $ par image | 0,037 $ par seconde en 768p |
Utilisez une image de départ PNG, JPEG, WebP ou GIF, dont chaque côté mesure entre 256 et 5 760 pixels, avec un rapport d’aspect entre 2:5 et 5:2 et un fichier de moins de 30 MB. Un PNG téléversé est l’entrée la plus simple. Si vous utilisez un lien public, il doit renvoyer l’image elle-même ; une page de partage d’un service cloud n’est pas une entrée image.
Exécutez le même cas avec Python
Utilisez Python 3.11 ou ultérieur, installez le paquet officiel avec python -m pip install spicyapi et disposez de l’utilitaire file. Définissez SPICY_API_KEY dans votre propre terminal, enregistrez ce code sous blue-mug.py et lancez python blue-mug.py. Il demande votre accord aux deux étapes payantes et s’interrompt pour la vérification de l’image. Gardez le dossier d’exécution créé pour reprendre ; ne choisissez un nouveau RUN_NAME que pour un nouvel essai délibéré. Le contrat API et les options SDK sont décrits dans la documentation du SDK Python.
import hashlib
import json
import os
from pathlib import Path
import subprocess
import tempfile
import time
import urllib.request
import uuid
from spicyapi import SpicyClient, output_assets
# Change RUN_NAME for a deliberately new pair of paid generations.
folder = Path(os.getenv("RUN_NAME", "blue-mug-1"))
folder.mkdir(exist_ok=True, mode=0o700)
state_file = folder / "state.json"
state = json.loads(state_file.read_text()) if state_file.exists() else {}
identity = hashlib.sha256(os.environ["SPICY_API_KEY"].encode()).hexdigest()
if state.get("identity", identity) != identity:
raise RuntimeError("This saved run belongs to a different API key.")
state["identity"] = identity
client = SpicyClient()
def save() -> None:
temp = state_file.with_suffix(".tmp")
temp.write_text(json.dumps(state, indent=2))
temp.chmod(0o600)
temp.replace(state_file)
def run(stage: str, model: str, inputs: dict) -> dict:
request = {"model": model, "input": inputs}
if stage not in state:
state[stage] = {"request": request, "key": str(uuid.uuid4())}
saved = state[stage]
if saved["request"] != request:
raise RuntimeError("Input changed: choose a new RUN_NAME, not a new retry key.")
save() # The same request and key survive a lost create response.
if not saved.get("taskId"):
if "first_sent" in saved and time.time() - saved["first_sent"] >= 23 * 3600:
raise RuntimeError("Unknown acceptance: reconcile task history before retrying.")
if "quote" not in saved:
quote = client.quote_task(model=model, input_data=inputs)
if input(f"{stage}: quoted ${quote['estimatedCost']}. Create? [y/N] ") != "y":
raise SystemExit("No task created for this stage.")
saved["quote"] = quote
saved.setdefault("first_sent", time.time())
save()
quote = saved["quote"]
task = client.create_task(model=model, input_data=inputs,
idempotency_key=saved["key"],
quote_id=quote["quoteId"],
expected_cost=quote["estimatedCost"])
saved["taskId"] = task["taskId"]
save()
task_id = saved["taskId"]
print(stage, "task:", task_id)
deadline = time.monotonic() + 1200
while time.monotonic() < deadline:
task = client.get_task(task_id)
if task["state"] in {"failed", "canceled", "expired"}:
raise RuntimeError(f"{task_id}: {task.get('errorCode')} {task.get('errorMessage')}")
if task["state"] == "succeeded":
assets = output_assets(task)
if not assets or assets[0].get("unavailable"):
raise RuntimeError(f"{task_id}: no retrievable output; keep the task ID.")
asset = assets[0]
if not asset.get("pending") and asset.get("url"):
print("Charge:", task.get("cost"), "settled:", task.get("settled"))
return asset
time.sleep(5)
raise TimeoutError(f"Resume {task_id} by rerunning this file; do not start another task.")
def download(asset: dict, destination: Path, mime: str) -> None:
temporary = None
try:
request = urllib.request.Request(asset["url"], headers={"User-Agent": "SpicyAPI-Blog-Example/1.0"})
with urllib.request.urlopen(request, timeout=300) as response:
if response.status != 200 or response.headers.get_content_type() != mime:
raise RuntimeError("Unexpected download status or content type.")
with tempfile.NamedTemporaryFile(dir=folder, delete=False) as handle:
temporary = Path(handle.name)
while chunk := response.read(1024 * 1024):
handle.write(chunk)
size = temporary.stat().st_size
expected = int(asset.get("bytes") or 0)
detected = subprocess.check_output(["file", "-b", "--mime-type", str(temporary)], text=True).strip()
if not size or (expected and size != expected) or detected != mime:
raise RuntimeError("Incomplete or incorrectly formatted download.")
temporary.replace(destination) # An interrupted transfer leaves the old file alone.
finally:
if temporary is not None:
temporary.unlink(missing_ok=True)
image_input = {
"prompt": (
'Photograph of a handmade blue ceramic mug of tea on a worn wooden workbench '
'in a pottery studio, a thin curl of steam rising from the mug. Soft window '
'light from the left, shallow depth of field, shelves of pots softly out of '
'focus behind. The mug sits left of centre with empty bench to its right. '
'Natural colours, no text, no logos.'
),
"aspect_ratio": "16:9",
"output_format": "png",
"seed": 42,
"enable_prompt_expansion": False,
}
image_asset = run("image", "alibaba/z-image-turbo/text-to-image", image_input)
download(image_asset, folder / "image.png", "image/png")
if input(f"Open {folder / 'image.png'}. Is the mug/frame approved? [y/N] ") != "y":
raise SystemExit("Stopped before video generation.")
if not state.get("image_uri"):
state["image_uri"] = client.upload_file(str(folder / "image.png"))["uri"]
save()
video_input = {"image_url": state["image_uri"], "prompt": 'Steam rises from the mug and drifts to the right. The camera pushes in slowly toward the mug, then holds. The mug, bench and shelves stay where they are. Quiet room, a faint ceramic clink.',
"duration_seconds": 5, "resolution": "768p"}
video_asset = run("video", "minimax/h3/image-to-video", video_input)
download(video_asset, folder / "video.mp4", "video/mp4")
print("Play", folder / "video.mp4", "with sound and review it before using it.")Gardez la clé API sur votre serveur ou un ordinateur que vous contrôlez, jamais dans une page web publique ou une application mobile. Consultez les pages des modèles ci-dessus pour vérifier la disponibilité et les entrées acceptées avant d’intégrer un autre modèle.
Confiez les appels à un agent si cela vous simplifie la tâche
Utilisez un Agent pour préparer les entrées et renvoyer le résultat pendant que vous validez le prix et les choix créatifs. Examinez vous-même l’image de départ avant d’autoriser la vidéo.
- CLI : enregistrez le même objet d’entrée dans
input.jsonet utiliseznpx @spicyapi/cli tasks create --model alibaba/z-image-turbo/text-to-image --input-file input.json --wait. La CLI confirme le prix ; gardez l’ID de tâche renvoyé pour la reprise. Pour le clip, téléversez l’image validée avecnpx @spicyapi/cli files upload image.png, puis créezminimax/h3/image-to-videode la même façon, avec l’URIspicy://renvoyée commeimage_url. - MCP : demandez à votre assistant d’utiliser
spicyapi_task_createpouralibaba/z-image-turbo/text-to-image, puisspicyapi_task_wait; pour le clip, ajoutezspicyapi_upload_filepour votreimage.pngvalidé, puisspicyapi_task_createavecminimax/h3/image-to-video, 5 secondes, 768p et le prompt de mouvement ci-dessus. Le serveur MCP demande confirmation du prix avant la création. - Skill : installez avec
npx skills add spicyapi-ai/spicy-skill, puis demandez à$spicyapid’exécuter le même flux et de s’arrêter pour votre accord. Le Skill utilise une connexion CLI ou MCP configurée.
Prévoyez le budget des essais, pas seulement du clip final
Ces estimations sont en dollars américains. Confirmez le prix en vigueur avant de générer.
| Requête | Prix estimé |
|---|---|
| Une image fixe Z-Image, PNG 16:9 | 0,010 $ |
| La même image avec extension du prompt | 0,016 $ |
| H3, 4 s en 768p | 0,148 $ |
H3, 5 s en 768p (valeur par défaut si resolution est omis) | 0,185 $ |
| H3, 5 s en 2k | 0,290 $ |
| H3, 10 s en 768p | 0,370 $ |
| H3, 15 s en 2k | 0,870 $ |
- Durée et résolution : H3 coûte 0,037 $ ou 0,058 $ par seconde en 768p ou 2k.
- Combinaisons non prises en charge : un clip de 4 secondes en 2k est refusé avec l’erreur
40004. - Deux brouillons : une image fixe et deux vidéos de cinq secondes en 768p coûtent 0,380 $.
- Brouillon puis prise de livraison : une image fixe, un contrôle de cinq secondes en 768p et une prise de cinq secondes en 2k coûtent 0,010 $ + 0,185 $ + 0,290 $ = 0,485 $, en réutilisant l’image validée.
L’argent est réservé à l’acceptation d’une tâche, puis le montant est réglé à sa fin. Les tâches échouées ou expirées libèrent normalement la réservation ; certains refus de contenu sont facturés selon la politique annoncée du modèle. Vérifiez le cost réel et l’état settled de la tâche lorsque vous examinez une facturation.
Corrigez le problème à la bonne étape
| Symptôme | Modification à apporter |
|---|---|
| La tasse a deux anses ou du texte parasite | Révisez le prompt d’image et validez une nouvelle image de départ avant de générer la vidéo |
| La tasse bouge alors que vous vouliez déplacer la caméra | Gardez l’image ; demandez un mouvement de caméra et explicitement une tasse immobile |
| La vidéo est bonne en 768p mais la nouvelle prise 2k diffère | Traitez-la comme une nouvelle prise et vérifiez le mouvement avant de l’accepter |
| Le lien de l’image échoue | Téléversez le fichier ou utilisez une URL d’image directe et accessible plutôt qu’une page de partage |
Le compte renvoie 401 ou 40310 | Vérifiez la clé ou terminez la vérification de l’adresse e-mail |
40301 / 40302 / 40303 | Vérifiez les permissions du modèle, la liste d’IP autorisées de la clé ou la disponibilité régionale |
40201 / 40202 | Vérifiez le solde disponible ou le plafond de dépenses du compte/de la clé ; gardez la requête sauvegardée lors du rapprochement d’un envoi échoué |
Une requête rejette seed, aspect_ratio ou output_format | Vérifiez les entrées acceptées par la tâche choisie ; ne copiez pas les champs de Z-Image dans H3 |
40004 | Changez la combinaison incompatible : 4 secondes en 2k |
40901 ou conflit d’idempotence | Demandez un nouveau devis : si aucun ID de tâche n’a été sauvegardé, supprimez l’entrée de cette étape dans state.json et relancez. Si l’essai précédent a pu être accepté, consultez votre liste de tâches avant toute autre création ; voir la référence des erreurs |
429 ou erreur de connexion | Gardez le dossier d’exécution et l’ID de tâche ; ne lancez pas un nouvel essai payant uniquement pour récupérer le résultat |
Une tâche signale content_rejected ou timeout | Lisez son erreur et son coût, puis choisissez délibérément un prompt modifié, une autre image source ou un plan plus court |
| La génération a réussi mais le résultat n’est pas prêt | Reprenez la même tâche sauvegardée ; ne générez pas un autre clip pour obtenir un téléchargement |
Gardez le premier flux de travail simple
Une fois le plan satisfaisant, choisissez sa durée et sa résolution de livraison, puis générez et vérifiez cette prise. Une image finale facultative peut guider la fin via last_image_url de H3 ; elle ne garantit ni une dernière image exacte ni le mouvement entre les images.
Pour une application, gardez les deux ID de tâche et stockez les fichiers validés dans votre propre stockage. Utilisez la comparaison entre interrogation et Webhooks pour choisir la collecte des résultats, le stockage des médias pour conserver les fichiers et la génération par lots pour de nombreux plans. Le premier jalon reste identique : une image validée et un clip qui suit réellement le plan voulu.







