Crie uma imagem, aprove e depois transforme essa imagem em um clipe curto. Estas etapas seguem nosso teste da caneca azul de outubro de 2026. Este guia usa uma imagem estática Z-Image de $0,010 e cinco segundos de MiniMax H3 em 768p por $0,185: $0,195 para uma tentativa em cada etapa, com expansão do prompt desativada na imagem estática. Experimente primeiro a mesma cena no Playground; quando as entradas funcionarem, use o exemplo Python ou deixe um agente fazer as chamadas.
O resultado desejado é específico: uma caneca bem formada sobre uma bancada, vapor indo para a direita e uma aproximação lenta da câmera que termina parada. Você não precisa implantar um modelo nem configurar um servidor GPU.
Experimente o caso completo no Playground
Crie sua conta e adicione saldo, e verifique seu e-mail. O Playground processa a solicitação; uma chave API só é necessária para o código e as ferramentas de agente abaixo.
1. Gere a imagem inicial
Abra Z-Image de texto para imagem e use o Playground. Cole este 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.
Escolha 16:9, PNG, seed 42, e desative a expansão do prompt. Confira o preço exibido e pressione Run model. A imagem básica custa $0,010; com expansão, custaria $0,016. Mantenha seed 42 fixo ao comparar mudanças no prompt.
Abra o resultado antes de continuar. Confira:
- Objeto: uma caneca, uma alça e geometria de cerâmica bem definida.
- Possibilidade de movimento: vapor visível e espaço à direita para ele se deslocar.
- Composição: a caneca fica à esquerda do centro; as prateleiras continuam como fundo discreto.
- Detalhes indesejados: sem letras soltas, logotipos ou canecas extras.
Baixe o PNG aprovado como image.png. Se a caneca estiver errada, mude uma frase e faça outra imagem antes de gastar com vídeo. Cada nova imagem básica acrescenta $0,010.
2. Anime a imagem aprovada
Abra MiniMax H3 de imagem para vídeo, adicione image.png em Input image e cole em 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.
Defina Duration como 5 segundos e Resolution como 768p; confira o preço e pressione Run model. Este clipe custa $0,185; escolher 2k custaria $0,290.
O prompt separa movimento do vapor, movimento da câmera e som. Ele pede que a caneca fique no lugar, em vez de descrevê-la novamente. Se ela atravessar a bancada, simplifique a instrução de câmera em vez de acrescentar ações ao objeto. Uma aproximação lenta move a câmera para mais perto; não pede que a caneca se mova.
3. Revise o clipe e escolha a próxima tomada
Reproduza o resultado inteiro com som. Confira se a caneca não desliza nem muda de forma, se a câmera se aproxima e depois para, se não há cortes indesejados e se o áudio combina com um ateliê tranquilo. A entrega esperada é um MP4 de cerca de cinco segundos com uma faixa de áudio; ter um arquivo reproduzível não torna a tomada utilizável por si só.
Se a imagem estava certa, mas o movimento errado, mantenha a imagem inicial aprovada e revise o prompt de movimento. Se a imagem inicial estava errada, substitua-a primeiro. H3 não tem seed: gerar em 2k produz uma nova tomada, que pode se mover de maneira diferente da versão 768p de que você gostou.
O que o teste da caneca azul produziu
A imagem estática custou $0,010 e chegou como PNG de 1280 × 720. A solicitação de vídeo de cinco segundos custou $0,125 (em 480p, que não é mais oferecido; o mesmo clipe em 768p custa $0,185) e retornou um MP4 de 832 × 480 com faixa de áudio. O total foi $0,135; em 768p seria $0,195.
A caneca ficou na bancada enquanto o vapor subia e ia para a direita; a aproximação da câmera foi sutil. A imagem posicionou a caneca apenas um pouco à esquerda do centro, e o vapor perdeu definição perto da prateleira no final. Para uma tomada de produto mais fechada, especifique melhor a posição da caneca no prompt da imagem antes de animá-la.
Por que estes dois modelos servem para a primeira tentativa
Z-Image oferece uma imagem barata para definir o assunto e a composição. H3 aceita essa imagem e gera movimento e trilha sonora. As entradas são diferentes:
| Z-Image de texto para imagem | H3 de imagem para vídeo | |
|---|---|---|
| ID do modelo | alibaba/z-image-turbo/text-to-image | minimax/h3/image-to-video |
| Entrada obrigatória | prompt (1 a 1.000 caracteres) | prompt (até 7.000) e image_url |
| Configurações usadas aqui | aspect_ratio (1:1, 4:3, 3:4, 16:9 ou 9:16), output_format (somente png), seed, enable_prompt_expansion | duration_seconds (4 a 15), resolution (768p ou 2k) |
| Não aceita | qualquer outro formato de saída | seed e aspect_ratio: a imagem inicial define o formato |
| Retorna | um PNG | um MP4 com trilha sonora gerada |
| Preço | $0,010 por imagem | $0,037 por segundo em 768p |
Use uma imagem inicial PNG, JPEG, WebP ou GIF, com cada lado entre 256 e 5.760 pixels, proporção entre 2:5 e 5:2 e arquivo menor que 30 MB. Um PNG enviado é a entrada mais simples. Se usar um link público de imagem, ele precisa retornar a imagem de fato; uma página de compartilhamento de um serviço na nuvem não serve como entrada de imagem.
Execute o mesmo caso em Python
Use Python 3.11 ou posterior, instale o pacote oficial com python -m pip install spicyapi e tenha o utilitário file disponível. Defina SPICY_API_KEY no seu terminal, salve isto como blue-mug.py e execute python blue-mug.py. O código pede aprovação nas duas etapas pagas e pausa para você revisar a imagem. Mantenha a pasta de execução gerada ao retomar; escolha um novo RUN_NAME apenas para uma tentativa intencionalmente nova. Para o contrato API e as opções SDK, consulte a documentação do 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.")Mantenha a chave API no seu servidor ou em uma máquina sob seu controle, nunca em uma página pública ou aplicativo móvel. As páginas dos modelos acima continuam sendo o local para conferir disponibilidade e entradas aceitas antes de integrar outro modelo.
Deixe um agente fazer as chamadas quando for mais fácil
Use um Agent para preparar as entradas e retornar o resultado enquanto você aprova o preço e as escolhas criativas. Revise pessoalmente a imagem inicial antes de autorizar o vídeo.
- CLI: salve o mesmo objeto de entrada como
input.jsone usenpx @spicyapi/cli tasks create --model alibaba/z-image-turbo/text-to-image --input-file input.json --wait. A CLI confirma o preço; guarde o ID da tarefa retornado para recuperação. Para o clipe, envie a imagem aprovada comnpx @spicyapi/cli files upload image.png, depois crieminimax/h3/image-to-videoda mesma forma, usando o URIspicy://retornado comoimage_url. - MCP: peça ao assistente para usar
spicyapi_task_createparaalibaba/z-image-turbo/text-to-image, depoisspicyapi_task_wait; para o clipe, adicionespicyapi_upload_filepara seuimage.pngaprovado, depoisspicyapi_task_createcomminimax/h3/image-to-video, 5 segundos, 768p e o prompt de movimento acima. O servidor MCP pede confirmação do preço antes da criação. - Skill: instale com
npx skills add spicyapi-ai/spicy-skill, depois peça a$spicyapipara executar o mesmo fluxo e parar para sua aprovação. O Skill usa uma conexão CLI ou MCP configurada.
Reserve orçamento para tentativas, não só para o clipe final
Estas estimativas estão em dólares americanos. Confirme o preço atual antes de gerar.
| Solicitação | Preço estimado |
|---|---|
| Uma imagem estática Z-Image, PNG 16:9 | $0,010 |
| Mesma imagem com expansão do prompt | $0,016 |
| H3, 4 s em 768p | $0,148 |
H3, 5 s em 768p (padrão se resolution for omitido) | $0,185 |
| H3, 5 s em 2k | $0,290 |
| H3, 10 s em 768p | $0,370 |
| H3, 15 s em 2k | $0,870 |
- Duração e resolução: H3 custa $0,037 ou $0,058 por segundo em 768p ou 2k.
- Combinações não aceitas: um clipe de 4 segundos em 2k é recusado com o erro
40004. - Dois rascunhos: uma imagem estática e dois vídeos de cinco segundos em 768p custam $0,380.
- Rascunho e tomada de entrega: uma imagem estática, uma verificação de cinco segundos em 768p e uma tomada de cinco segundos em 2k custam $0,010 + $0,185 + $0,290 = $0,485, reutilizando a imagem aprovada.
O dinheiro fica reservado quando uma tarefa é aceita e é liquidado quando ela termina. Tarefas com falha ou expiradas normalmente liberam a reserva; certas recusas de conteúdo são cobradas conforme a política informada do modelo. Confira o cost real e o estado settled da tarefa ao investigar uma cobrança.
Corrija o problema na etapa certa
| Sintoma | O que mudar |
|---|---|
| A caneca tem duas alças ou texto solto | Revise o prompt da imagem e aprove outra imagem inicial antes de gerar vídeo |
| A caneca se move quando você queria mover a câmera | Mantenha a imagem; peça um movimento de câmera e deixe explícito que a caneca deve ficar parada |
| O vídeo fica bom em 768p, mas a nova tomada 2k é diferente | Trate-a como uma nova tomada e confira o movimento antes de aceitá-la |
| O link da imagem falha | Envie o arquivo ou use uma URL direta e acessível de imagem, não uma página de compartilhamento |
A conta retorna 401 ou 40310 | Confira a chave ou conclua a verificação do e-mail |
40301 / 40302 / 40303 | Confira permissões do modelo, lista de IPs permitidos da chave ou disponibilidade regional |
40201 / 40202 | Confira saldo disponível ou limite de gastos da conta/chave; mantenha a solicitação salva ao reconciliar um envio com falha |
Uma solicitação rejeita seed, aspect_ratio ou output_format | Confira as entradas aceitas pela tarefa selecionada; não copie campos do Z-Image para H3 |
40004 | Mude a combinação incompatível: 4 segundos em 2k |
40901 ou conflito de idempotência | Peça nova cotação: se nenhum ID de tarefa foi salvo, remova a entrada daquela etapa em state.json e execute novamente. Se a tentativa anterior pode ter sido aceita, confira sua lista de tarefas antes de criar qualquer outra; veja a referência de erros |
429 ou erro de conexão | Mantenha a pasta de execução e o ID da tarefa; não inicie outra tentativa paga só para recuperar o resultado |
Uma tarefa informa content_rejected ou timeout | Leia o erro e a cobrança e escolha deliberadamente um prompt alterado, outra imagem de origem ou uma tomada mais curta |
| A geração terminou, mas o resultado não está pronto | Retome a mesma tarefa salva; não gere outro clipe para obter um download |
Mantenha pequeno o primeiro fluxo de trabalho
Quando a tomada funcionar, defina a duração e a resolução de entrega, depois gere e revise essa tomada. Uma imagem final opcional pode orientar o encerramento por last_image_url do H3; ela não garante o último quadro exato nem o movimento entre quadros.
Para um aplicativo, mantenha os dois IDs de tarefa e salve os arquivos aprovados no seu próprio armazenamento. Use a comparação entre sondagem e Webhooks para escolher a coleta de resultados, o armazenamento de mídia para guardar arquivos e a geração em lote quando precisar de muitas tomadas. O primeiro marco continua o mesmo: uma imagem aprovada e um clipe que realmente siga a tomada pretendida.







