Tutorial
URL de saída da tarefa HiAPI expirada (expireAt): baixe e transfira o resultado antes que ele expire
O output[].url no estado final traz expireAt; ao expirar, retorna 404 — como identificar, baixar a tempo e recuperar após a expiração.
Você gera uma imagem ou vídeo com POST /v1/tasks e, após a tarefa concluir, obtém de data.output[0].url um link de temp.hiapi.ai — que abre na hora, mas ao acessar alguns dias depois vira uma página HTML de "Not Found":
HTTP/2 404
content-type: text/html
O retorno não são os bytes da imagem/vídeo, mas a página 404 da Cloudflare. O motivo é um só: a URL de saída da tarefa é um link temporário com validade; assim que o expireAt (timestamp Unix) do estado final chega, o objeto em temp.hiapi.ai é removido e o hotlink deixa de funcionar.
Primeiro, entenda os campos do estado final
Quando o polling do estado final da tarefa (GET /v1/tasks/{taskId}) é bem-sucedido, os campos-chave ficam assim:
{
"code": 200,
"data": {
"taskId": "tk-hiapi-xxxxxxxx",
"status": "success",
"storage": "temp",
"created": 1791828181,
"completed": 1791828201,
"output": [
{
"artifactId": "...",
"type": "image",
"url": "https://temp.hiapi.ai/....png",
"expireAt": 1792432981
}
]
},
"message": "success"
}
data.status == "success"é o estado final de sucesso (falha é"fail"; em fila/processando sãoqueued/handling/archiving).data.outputé um array, e cada elemento traz seu própriourleexpireAt.expireAté um timestamp Unix inteiro (não é string ISO). Para saber se expirou, olhe só para ele:now >= expireAtsignifica expirado.- O valor de
data.storageé"temp"— o que já é uma dica: é armazenamento temporário, não para referência de longo prazo.
Qual é a duração da validade? Em uma amostra medida,
expireAt - completed = 604800segundos (exatamente 7 dias). Mas não hardcode "7 dias" no código — use oexpireAtde cada saída como referência, pois modelos/estratégias de armazenamento diferentes podem variar.
Causas comuns (ordenadas por probabilidade)
- Salvar o hotlink no banco de dados para referência de longo prazo: ao persistir, guarda-se a URL temporária
temp.hiapi.ai/..., e após oexpireAttudo vira 404 em massa. Esta é a armadilha mais comum. - Receber o estado final e não baixar imediatamente: buscar os bytes muito tempo depois (além do TTL) da conclusão da tarefa.
- Achar que refazer o polling "renova" o link: fazer GET de novo no mesmo
taskIdretorna a mesma url e o mesmoexpireAt— os metadados persistem, mas o objeto em si não é regenerado após expirar; um novo GET não dá um novo link. - Confundir 404 com falha da tarefa: a tarefa na verdade já estava
successhá tempos, apenas o asset de saída expirou; são duas coisas distintas.
Passos de correção
- Assim que o estado final chegar, baixe os bytes imediatamente. Após o polling retornar
data.status == "success", faça GET em cadaoutput[].urle baixe o binário na hora — não enfileire, não adie. - Transfira para o seu próprio armazenamento de objetos (R2 / S3 / disco local), trocando por uma sua URL persistente.
- No banco de dados, guarde apenas a sua URL persistente; nunca armazene o hotlink
temp.hiapi.ai. - Antes de baixar, valide se não expirou: baixe só se
now < expireAt; se já expirou, vá direto para a lógica de reexecução. - Se expirou, só resta reexecutar a tarefa. Não existe "trocar taskId por um novo link" — um novo GET no mesmo taskId retorna a mesma url inválida. Para obter novamente um resultado baixável, é preciso reenviar a tarefa (nova cobrança).
- Em tarefas em lote, controle a ordem dos downloads: a primeira a concluir no lote tem o
expireAtmais cedo. Baixe imediatamente na ordem de conclusão; não espere o lote inteiro terminar para baixar tudo de uma vez, senão as que concluíram antes podem estar perto de expirar.
Exemplo mínimo de verificação
Confirme que sua key funciona, que a API de tarefas retorna resultado e veja o expireAt com seus próprios olhos. Use o model id puro (z-image, sem sufixos como /text-to-image, que causam 400):
# 1. Enviar a tarefa
curl -s https://api.hiapi.ai/v1/tasks \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "z-image",
"input": { "prompt": "a red apple on a wooden table" }
}'
# → {"code":200,"data":{"taskId":"tk-hiapi-..."},"message":"success"}
# 2. Fazer polling do estado final com o taskId do passo anterior e ler url e expireAt
curl -s https://api.hiapi.ai/v1/tasks/tk-hiapi-xxxx \
-H "Authorization: Bearer $HIAPI_API_KEY"
# depois que data.status vira "success", data.output[0].url / .expireAt já estão disponíveis
Se a autenticação falhar, o retorno é HTTP 401 + {"error":{"code":"permission_denied","type":"hiapi_error","request_id":"..."}} — é problema de key, não de expiração. Se a tarefa não for encontrada, é {"code":404,"data":null,"message":"task not found"} — geralmente taskId copiado errado ou key de ambiente errado.
Links relacionados
- Como enviar e fazer polling de tarefas: Documentação de início rápido da HiAPI
- Consultar sua API key e ver o histórico de tarefas: Console
- Quais modelos podem ser chamados: Marketplace de modelos
- Exemplo de página de um modelo (o usado na verificação deste artigo): Z-Image texto-para-imagem
FAQ
P: Qual é o formato exato do expireAt?
R: Timestamp Unix inteiro (segundos), por exemplo 1792432981, não é string ISO. Basta usar now >= expireAt para saber se expirou.
P: Depois que o link expira, dá para fazer GET de novo na tarefa para "renovar" o link?
R: Não. Um novo GET no mesmo taskId retorna a mesma url original e o mesmo expireAt, e o objeto já foi removido. Para obter novamente um resultado baixável, só reexecutando a tarefa (nova cobrança).
P: Após expirar, o acesso retorna 403 ou 404?
R: É HTTP 404, e o body é uma página "Not Found" da Cloudflare em text/html — não são bytes de imagem, nem um AccessDenied em XML. Se sua lógica de download só aceita 200, lembre-se de tratar "qualquer coisa diferente de 200 como falha".
P: A validade é fixa em 7 dias?
R: Na amostra medida, 7 dias (604800 segundos), mas use o expireAt de cada saída como referência e não hardcode o número de dias no código.
P: Como baixar um lote de tarefas da forma mais segura?
R: Na ordem de conclusão: quem fica success primeiro tem os bytes baixados primeiro. As saídas que concluem antes têm o expireAt mais cedo; deixar para baixar tudo no final tende a esbarrar em expiração iminente.
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