Crea un’immagine, approvala e poi animala in un breve clip. Questi passaggi seguono il nostro test della tazza blu di ottobre 2026. La guida usa un’immagine fissa Z-Image da 0,010 $ e cinque secondi di MiniMax H3 a 768p da 0,185 $: 0,195 $ per un tentativo in ogni fase, con espansione del prompt disattivata per l’immagine fissa. Prova prima la stessa scena nel Playground; quando gli input funzionano, usa l’esempio Python o lascia che un agente gestisca le chiamate.
Il risultato che cerchi è preciso: una tazza ben formata su un banco da lavoro, vapore che si sposta a destra e un lento avanzamento della camera che termina da ferma. Non devi distribuire un modello né configurare un server GPU.
Prova il caso completo nel Playground
Crea il tuo account e aggiungi saldo, poi verifica l’e-mail. Il Playground gestisce la richiesta; una chiave API serve solo per il codice e gli strumenti per agenti qui sotto.
1. Genera l’immagine iniziale
Apri Z-Image da testo a immagine e usa il suo Playground. Incolla questo 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.
Scegli 16:9, PNG, seed 42, e disattiva l’espansione del prompt. Controlla il prezzo mostrato e premi Run model. L’immagine base costa 0,010 $; l’espansione porterebbe il prezzo a 0,016 $. Mantieni seed 42 fisso mentre confronti le modifiche al prompt.
Apri il risultato prima di continuare. Controlla:
- Oggetto: una tazza, un manico e geometria ceramica pulita.
- Possibilità di movimento: vapore visibile e spazio a destra per il suo spostamento.
- Composizione: la tazza è a sinistra del centro; gli scaffali restano uno sfondo discreto.
- Dettagli indesiderati: nessuna scritta casuale, logo o tazza aggiuntiva.
Scarica il PNG approvato come image.png. Se la tazza è sbagliata, modifica una frase e crea una nuova immagine prima di spendere per il video. Ogni nuova immagine base aggiunge 0,010 $.
2. Anima l’immagine approvata
Apri MiniMax H3 da immagine a video, aggiungi image.png come Input image e incolla in 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.
Imposta Duration a 5 secondi e Resolution a 768p; controlla il prezzo e premi Run model. Questo clip costa 0,185 $; scegliendo 2k costerebbe 0,290 $.
Il prompt separa movimento del vapore, movimento della camera e suono. Chiede alla tazza di restare ferma invece di descriverla di nuovo. Se la tazza attraversa il banco, semplifica l’istruzione della camera invece di aggiungere azioni all’oggetto. Un lento avanzamento avvicina la camera; non chiede alla tazza di muoversi.
3. Controlla il clip e scegli la prossima ripresa
Riproduci l’intero risultato con l’audio. Controlla che la tazza non scivoli né cambi forma, che la camera avanzi e poi si fermi, che non ci siano stacchi indesiderati e che l’audio sia adatto a un laboratorio tranquillo. Il risultato atteso è un MP4 di circa cinque secondi con una traccia audio; un file riproducibile non basta a rendere la ripresa utilizzabile.
Se l’immagine era corretta ma il movimento no, conserva l’immagine iniziale approvata e modifica il prompt di movimento. Se era sbagliata l’immagine iniziale, sostituiscila prima. H3 non ha seed: generare a 2k produce una nuova ripresa, che può muoversi diversamente dalla versione 768p che ti piaceva.
Cosa ha prodotto il test della tazza blu
L’immagine fissa è costata 0,010 $ ed è arrivata come PNG da 1280 × 720. La richiesta video di cinque secondi è costata 0,125 $ (a 480p, non più offerto; lo stesso clip a 768p costa 0,185 $) e ha restituito un MP4 da 832 × 480 con traccia audio. Il totale è stato 0,135 $; a 768p sarebbe 0,195 $.
La tazza è rimasta sul banco mentre il vapore saliva verso destra; l’avanzamento della camera era delicato. L’immagine fissa collocava la tazza solo leggermente a sinistra del centro e il vapore si attenuava vicino allo scaffale verso la fine. Per una ripresa di prodotto più stretta, specifica meglio la posizione della tazza nel prompt dell’immagine prima di animarla.
Perché questi due modelli sono adatti al primo tentativo
Z-Image ti offre un’immagine economica per definire soggetto e composizione. H3 accetta quell’immagine e genera movimento e colonna sonora. I loro input sono diversi:
| Z-Image da testo a immagine | H3 da immagine a video | |
|---|---|---|
| ID del modello | alibaba/z-image-turbo/text-to-image | minimax/h3/image-to-video |
| Input richiesto | prompt (da 1 a 1.000 caratteri) | prompt (fino a 7.000) e image_url |
| Impostazioni usate qui | aspect_ratio (1:1, 4:3, 3:4, 16:9 o 9:16), output_format (solo png), seed, enable_prompt_expansion | duration_seconds (da 4 a 15), resolution (768p o 2k) |
| Non accettato | qualsiasi altro formato di output | seed e aspect_ratio: l’immagine iniziale determina la forma |
| Restituisce | un PNG | un MP4 con colonna sonora generata |
| Prezzo | 0,010 $ per immagine | 0,037 $ al secondo a 768p |
Usa un’immagine iniziale PNG, JPEG, WebP o GIF, con ciascun lato tra 256 e 5.760 pixel, rapporto d’aspetto tra 2:5 e 5:2 e file sotto 30 MB. Un PNG caricato è l’input più semplice. Se usi un link pubblico, deve restituire l’immagine vera e propria; una pagina di condivisione cloud non è un input immagine.
Esegui lo stesso caso da Python
Usa Python 3.11 o successivo, installa il pacchetto ufficiale con python -m pip install spicyapi e assicurati che l’utilità file sia disponibile. Imposta SPICY_API_KEY nel tuo terminale, salva questo codice come blue-mug.py ed esegui python blue-mug.py. Chiede approvazione in entrambe le fasi a pagamento e si ferma per il controllo dell’immagine. Conserva la cartella di esecuzione generata quando riprendi; scegli un nuovo RUN_NAME solo per un tentativo deliberatamente nuovo. Per il contratto API e le opzioni SDK, consulta la documentazione del 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.")Conserva la chiave API sul tuo server o su un computer che controlli, mai in una pagina pubblica o in un’app mobile. Le pagine dei modelli indicate sopra restano il riferimento per verificare disponibilità e input accettati prima di integrare un altro modello.
Lascia le chiamate a un agente quando è più comodo
Usa un Agent se vuoi che prepari gli input e restituisca il risultato mentre tu approvi prezzo e scelte creative. Controlla personalmente l’immagine iniziale prima di autorizzare il video.
- CLI: salva lo stesso oggetto di input come
input.jsone usanpx @spicyapi/cli tasks create --model alibaba/z-image-turbo/text-to-image --input-file input.json --wait. La CLI conferma il prezzo; conserva l’ID attività restituito per il recupero. Per il clip, carica l’immagine approvata connpx @spicyapi/cli files upload image.png, poi creaminimax/h3/image-to-videoallo stesso modo usando l’URIspicy://restituito comeimage_url. - MCP: chiedi all’assistente di usare
spicyapi_task_createperalibaba/z-image-turbo/text-to-image, poispicyapi_task_wait; per il clip, aggiungispicyapi_upload_fileper il tuoimage.pngapprovato, poispicyapi_task_createconminimax/h3/image-to-video, 5 secondi, 768p e il prompt di movimento sopra. Il server MCP richiede conferma del prezzo prima della creazione. - Skill: installa con
npx skills add spicyapi-ai/spicy-skill, poi chiedi a$spicyapidi eseguire lo stesso flusso e fermarsi per la tua approvazione. Lo Skill usa una connessione CLI o MCP configurata.
Prevedi un budget per i tentativi, non solo per il clip finale
Queste stime sono in dollari statunitensi. Conferma il prezzo corrente prima di generare.
| Richiesta | Prezzo stimato |
|---|---|
| Un’immagine fissa Z-Image, PNG 16:9 | 0,010 $ |
| Stessa immagine con espansione del prompt | 0,016 $ |
| H3, 4 s a 768p | 0,148 $ |
H3, 5 s a 768p (valore predefinito se ometti resolution) | 0,185 $ |
| H3, 5 s a 2k | 0,290 $ |
| H3, 10 s a 768p | 0,370 $ |
| H3, 15 s a 2k | 0,870 $ |
- Durata e risoluzione: H3 costa 0,037 $ o 0,058 $ al secondo a 768p o 2k.
- Combinazioni non supportate: un clip di 4 secondi a 2k viene rifiutato con errore
40004. - Due bozze: un’immagine fissa più due video di cinque secondi a 768p costano 0,380 $.
- Bozza e ripresa finale: un’immagine fissa, un controllo di cinque secondi a 768p e una ripresa di cinque secondi a 2k costano 0,010 $ + 0,185 $ + 0,290 $ = 0,485 $, riutilizzando l’immagine approvata.
Il denaro viene riservato all’accettazione dell’attività e il costo viene regolato quando termina. Le attività fallite e scadute normalmente liberano la somma riservata; alcuni rifiuti di contenuto vengono addebitati secondo la politica dichiarata del modello. Controlla il cost effettivo e lo stato settled dell’attività quando verifichi un addebito.
Risolvi il problema nella fase giusta
| Sintomo | Cosa cambiare |
|---|---|
| La tazza ha due manici o testo casuale | Modifica il prompt immagine e approva una nuova immagine iniziale prima di generare il video |
| La tazza si muove quando volevi muovere la camera | Conserva l’immagine; chiedi un movimento di camera e specifica che la tazza resti ferma |
| Il video è buono a 768p ma la nuova ripresa 2k differisce | Considerala una nuova ripresa e controlla il movimento prima di accettarla |
| Il link dell’immagine non funziona | Carica il file o usa un URL diretto e raggiungibile dell’immagine, non una pagina di condivisione |
L’account restituisce 401 o 40310 | Controlla la chiave o completa la verifica dell’e-mail |
40301 / 40302 / 40303 | Controlla i permessi del modello, l’elenco IP consentiti della chiave o la disponibilità regionale |
40201 / 40202 | Controlla il saldo disponibile o il limite di spesa di account/chiave; conserva la richiesta salvata quando riconcili un invio fallito |
Una richiesta rifiuta seed, aspect_ratio o output_format | Controlla gli input supportati dall’attività scelta; non copiare i campi Z-Image in H3 |
40004 | Cambia la combinazione incompatibile: 4 secondi a 2k |
40901 o conflitto di idempotenza | Richiedi un nuovo preventivo: se non hai salvato un ID attività, elimina la voce di quella fase in state.json e riesegui. Se il tentativo precedente potrebbe essere stato accettato, controlla l’elenco attività prima di creare altro; consulta il riferimento degli errori |
429 o errore di connessione | Conserva cartella di esecuzione e ID attività; non avviare un altro tentativo a pagamento solo per recuperare il risultato |
Un’attività segnala content_rejected o timeout | Leggi errore e addebito, poi scegli deliberatamente un prompt modificato, un’altra immagine sorgente o una ripresa più breve |
| Generazione riuscita ma risultato non pronto | Riprendi la stessa attività salvata; non generare un altro clip per ottenere un download |
Mantieni semplice il primo flusso di lavoro
Quando la ripresa funziona, decidi durata e risoluzione di consegna, poi genera e controlla quella ripresa. Un’immagine finale facoltativa può guidare la chiusura tramite last_image_url di H3; non garantisce un ultimo frame esatto né il movimento tra i frame.
Per un’applicazione, conserva i due ID attività e salva i file approvati nel tuo archivio. Usa il confronto tra polling e Webhook per scegliere come raccogliere i risultati, l’archiviazione dei media per conservare i file e la generazione in batch quando servono molte riprese. Il primo traguardo resta uguale: un’immagine approvata e un clip che segua davvero la ripresa prevista.







