Tutorial
Crie aplicações de IA multimodelo no hiapi com uma única API unificada
- Modelo
- seedance-2.5
O que você vai construir
A maioria das equipes acaba recorrendo a três ou quatro provedores de IA diferentes para um único produto: um modelo de texto-para-imagem para miniaturas, um modelo de texto-para-vídeo para clipes curtos para redes sociais e, talvez, um modelo TTS para narração. Cada provedor tem seu próprio SDK, seu próprio esquema de autenticação, sua própria lógica de polling. O hiapi reúne tudo isso em um único padrão de endpoint: POST /v1/tasks para criar uma tarefa, GET /v1/tasks/:id para consultá-la, e isso vale para todos os modelos de geração da plataforma.
Este tutorial demonstra esse padrão de integração com três modelos reais e atualmente disponíveis — z-image (texto-para-imagem), qwen-image-2.0 (texto-para-imagem, com schema diferente) e seedance-2.5 (texto-para-vídeo) — para que você perceba claramente onde a API é idêntica e onde apenas o objeto input de cada modelo difere.
Pré-requisitos
- Uma API key do hiapi obtida no console
curlou qualquer cliente HTTP capaz de enviar JSON- Nenhum SDK necessário — todos os exemplos abaixo são chamadas HTTP puras
Cada requisição exige um cabeçalho:
Authorization: Bearer <your-api-key>
Se a key for inválida ou estiver ausente, um erro da classe 401 é retornado antes de qualquer modelo ser acionado:
{
"error": {
"code": "permission_denied",
"message": "invalid api key",
"request_id": "req_...",
"type": "hiapi_error"
}
}
Não existe modo sem key nem modo anônimo — toda chamada a /v1/tasks precisa carregar um bearer token válido. Para rotação e escopo de keys, consulte Autenticação.
Exemplo mínimo executável
1. Criar uma tarefa
A estrutura da chamada de criação é sempre a mesma: um id de model e um objeto input específico do modelo. Veja abaixo a criação de uma tarefa de imagem com z-image:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"aspect_ratio": "16:9"
}
}'
A resposta volta imediatamente — a geração é assíncrona:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "queued"
}
}
Agora o mesmo padrão com qwen-image-2.0. Mesmo endpoint, mesma estrutura externa — apenas o input muda (este modelo recebe uma string size em vez de aspect_ratio):
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-image-2.0",
"input": {
"prompt": "a minimalist workspace, soft morning light, isometric illustration",
"size": "2048*2048"
}
}'
E agora uma tarefa de vídeo com seedance-2.5/text-to-video — novamente a mesma estrutura externa, agora com duration e um enum de aspect_ratio mais amplo:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "seedance-2.5/text-to-video",
"input": {
"prompt": "a paper airplane gliding through a sunlit office, slow motion",
"duration": 5,
"aspect_ratio": "16:9"
}
}'
Esse é o ponto central: a estrutura externa da tarefa nunca muda; só o input varia. Cada modelo no hiapi documenta seu próprio schema de input na página do modelo — consulte sempre o schema do modelo específico antes de construir uma nova integração, porque campos como aspect_ratio, size e duration não são intercambiáveis entre modelos.
2. Consultar o resultado
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxxxxxxxxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
O status passa por queued → handling → archiving até chegar ao estado final success ou fail. Em caso de sucesso, data.output é um array — a maioria dos modelos de imagem/vídeo retorna um único item, mas uma tarefa pode retornar vários tipos de saída (por exemplo, um modelo de vídeo retornando simultaneamente video e thumbnail):
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "success",
"output": [
{ "type": "image", "url": "https://...", "expireAt": "2026-10-09T00:00:00Z" }
]
}
}
expireAt é importante — por padrão, as URLs de saída são temporárias. Baixe o conteúdo assim que a tarefa for concluída (ou reenvie para seu próprio storage). Se precisar que a plataforma retenha os arquivos por mais tempo, defina storage: "persistent" na criação ou faça upgrade depois — consulte Armazenamento.
Em caso de falha, você recebe um objeto error em vez de output:
{
"code": 200,
"message": "ok",
"data": {
"taskId": "tk-hiapi-xxxxxxxxxxxx",
"status": "fail",
"error": { "code": "...", "message": "..." }
}
}
O loop de polling mínimo em Python abaixo funciona igualmente para todos os modelos acima:
import time
import requests
def run_task(model: str, input_payload: dict, api_key: str) -> list[dict]:
headers = {"Authorization": f"Bearer {api_key}"}
create = requests.post(
"https://api.hiapi.ai/v1/tasks",
headers=headers,
json={"model": model, "input": input_payload},
).json()
task_id = create["data"]["taskId"]
time.sleep(3) # espera inicial antes do primeiro poll
while True:
poll = requests.get(
f"https://api.hiapi.ai/v1/tasks/{task_id}", headers=headers
).json()
status = poll["data"]["status"]
if status == "success":
return poll["data"]["output"]
if status == "fail":
raise RuntimeError(poll["data"]["error"])
time.sleep(4) # 3-5s entre polls; use 5-10s para modelos de vídeo
Padrões para produção
Use callbacks em vez de polling em produção
O polling funciona bem para scripts ou ferramentas de CLI, mas em qualquer escala real de tráfego o callback é preferível: passe um objeto callback ao criar a tarefa e o hiapi fará um POST do resultado final para seu endpoint, em vez de você precisar repetir GET /v1/tasks/:id em um loop.
{
"model": "z-image",
"input": { "prompt": "...", "aspect_ratio": "1:1" },
"callback": { "url": "https://yourapp.com/webhooks/hiapi", "when": "final" }
}
when: "final" significa que você receberá apenas um POST, disparado no estado final (success ou fail) — sem o ruído dos estados intermediários handling/archiving. Se você configurou um callback mas não recebeu nada, verifique se seu endpoint é publicamente acessível e retorna 2xx rapidamente; para conhecer a causa mais comum de callbacks perdidos silenciosamente, consulte este artigo relacionado.
Retentativas idempotentes
Instabilidades de rede são inevitáveis. Se você repetir uma chamada POST /v1/tasks após um timeout sem proteção, pode acabar criando (e pagando por) tarefas duplicadas. Envie um cabeçalho Idempotency-Key (qualquer string de até 255 bytes, normalmente um UUID gerado por requisição lógica); retentativas com a mesma key retornam a tarefa original em vez de criar uma nova:
curl -s -X POST https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Idempotency-Key: 8f14e45f-ceea-467e-bd5f-...-9a2c" \
-H "Content-Type: application/json" \
-d '{"model": "z-image", "input": {"prompt": "...", "aspect_ratio": "1:1"}}'
Trate erros de validação por modelo
Como cada modelo tem seu próprio schema de input, o modo de falha mais comum é um 400 em um campo — um campo válido para um modelo e inválido para outro, por exemplo, enviar aspect_ratio para um modelo que espera size:
{
"code": 400,
"data": null,
"error_code": "INVALID_REQUEST",
"message": "invalid input: aspect_ratio: value must be one of '1:1', '4:3', '3:4', '16:9', '9:16'"
}
Se você estiver construindo um roteador multimodelo (permitindo que o usuário ou sua própria lógica escolha o modelo em tempo de execução), mantenha um pequeno registro de construtores de input por modelo, em vez de uma estrutura de payload compartilhada:
MODEL_INPUT_BUILDERS = {
"z-image": lambda prompt, **kw: {
"prompt": prompt, "aspect_ratio": kw.get("aspect_ratio", "1:1")
},
"qwen-image-2.0": lambda prompt, **kw: {
"prompt": prompt, "size": kw.get("size", "2048*2048")
},
"seedance-2.5/text-to-video": lambda prompt, **kw: {
"prompt": prompt,
"duration": kw.get("duration", 5),
"aspect_ratio": kw.get("aspect_ratio", "16:9"),
},
}
def build_task_payload(model: str, prompt: str, **kw) -> dict:
return {"model": model, "input": MODEL_INPUT_BUILDERS[model](prompt, **kw)}
Assim, as particularidades de cada modelo ficam concentradas em um único lugar, em vez de espalhadas por cada ponto de chamada. Um taskId inexistente ou de outra conta retorna um 404 comum:
{ "code": 404, "message": "task not found", "data": null }
Já um 503 temporário deve ser reprocessado com backoff exponencial, em vez de ser exposto diretamente ao usuário — limites de taxa e de capacidade estão documentados em Limites de taxa.
Leitura relacionada
- Referência da API de tarefas assíncronas — o contrato completo de
create/detail - Autenticação — escopo e rotação de API keys
- Armazenamento — manter as saídas além do tempo de expiração padrão
- Por que seu callback de tarefa pode não estar disparando
- Explorar todos os modelos — o schema de
inputde cada modelo está na respectiva página - Preços — o custo é calculado por modelo e por saída, não com preço único por requisição
Perguntas frequentes
Cada modelo exige um SDK ou uma API key diferente?
Não. Uma API key e um padrão de endpoint (/v1/tasks) servem para todos os modelos da plataforma. Apenas o objeto input do corpo da requisição muda.
Por que a resposta logo após criar a tarefa ainda não tem output?
A geração é assíncrona. A chamada de criação retorna apenas taskId e status: "queued" — você precisa consultar GET /v1/tasks/:id (ou usar callback) até que o status atinja success ou fail.
Posso chamar a API sem autenticação para um teste rápido?
Não — toda chamada a /v1/tasks exige uma key válida em Authorization: Bearer. Não há modo sem key nem modo de demonstração para chamadas de produção; gere uma key primeiro no console.
O que acontece se eu consultar depois que a URL de saída expirar?
O registro da tarefa em si permanece, mas o output[].url de tarefas não persistentes deixa de ser resolvível após o expireAt. Baixe os arquivos imediatamente após o sucesso ou, se precisar de retenção de longo prazo, defina storage: "persistent" na criação.
Como descubro quais campos um modelo aceita?
Consulte a página do modelo em Modelos — cada uma documenta o schema exato de input. Não presuma que aspect_ratio, size ou duration são intercambiáveis entre modelos; como mostrado acima, z-image e qwen-image-2.0 já divergem na forma de expressar o formato da imagem.
Artigos relacionados
Ver todos os artigos →-
Tutorial
Como chamar hailuo-2.3-fast/image-to-video pela API HiAPI: curl e Python na prática
A primeira requisição executável do nível rápido e econômico de geração de vídeo a partir de imagem — do ID do modelo ao download do MP4
-
Tutorial
Como chamar hailuo-2.3/text-to-video pela API HiAPI: curl, Python e exemplos de requisição executáveis
-
Tutorial
Usar Kandinsky 6.0 Pro text-to-video na HiAPI: tutorial de curl e Python
Demonstração prática com foco na requisição: montar a chamada de prompt para clipe de vídeo, enviar a requisição, consultar o status de conclusão e baixar o resultado