HiAPI
Blogue

Tutorial

Como usar kling-3.0-omni/image-to-video via API HiAPI: guia com curl e Python

Nov 3, 2026
扁平液状色块交叠的抽象封面图,墨色底上点缀青瓷绿与石板灰,用于该教程文章。

O que o endpoint image-to-video faz

A geração de vídeo não é uma chamada síncrona. Na HiAPI, kling-3.0-omni/image-to-video segue o padrão enviar primeiro, depois consultar: você envia uma requisição HTTP que descreve a tarefa, a API aceita a requisição e retorna um identificador de tarefa, e então você consulta essa tarefa até que a geração do vídeo seja concluída.

Essa estrutura em duas etapas tem uma razão de ser. Renderizar um vídeo a partir de uma imagem estática leva tempo — muito mais do que uma requisição HTTP típica deveria permanecer aberta. Se o endpoint bloqueasse até o arquivo estar pronto, cada chamada sofreria timeout de gateway e quedas de conexão. Ao retornar imediatamente um handle de tarefa, a API deixa seu código decidir quanto tempo esperar, com que frequência verificar e como lidar com erros.

Uma tarefa típica contém três partes lógicas:

  • Mídia de entrada — a imagem (ou conjunto de imagens) que ancora o plano.
  • Configurações de geração — texto do prompt, opções relacionadas a movimento, áudio e escolha de resolução.
  • Ciclo de vida — a resposta de envio contendo o ID da tarefa, seguida de verificações de status que produzem a URL final do vídeo ou o motivo da falha.

É útil imaginar esse endpoint como uma fila com recibo. A chamada de envio é você entregando a ordem de serviço; o recibo é o ID da tarefa; a chamada de consulta é você perguntando no balcão se o pedido está pronto. Nenhum vídeo em si é retornado no envio, então qualquer cliente que você escrever deve ser organizado em torno dessas duas fases desde o início.

Duas consequências decorrem disso. Primeiro, seu tratamento de erros deve ser separado: um corpo de requisição rejeitado é um problema no envio, corrigível no código; já uma falha de renderização é um problema no nível da tarefa, que não será resolvido por mais vezes que você reenvie o mesmo corpo. Segundo, você precisa de algum lugar para guardar o ID da tarefa — uma linha de log, uma linha no banco de dados, uma variável no script — porque ele é o único handle que você tem do trabalho em andamento.

Com esse modelo mental estabelecido, o restante deste guia é essencialmente trabalho de encanamento: construir a requisição e depois esperar por ela corretamente.

Construindo uma requisição curl funcional

Comece com a menor requisição possível que funcione, confirme que ela é aceita e só então adicione opções. Os nomes exatos dos campos JSON e o caminho do endpoint são definidos na página de referência do modelo na HiAPI — consulte essa página para obter o schema atual, em vez de confiar em trechos de posts de blog (incluindo este).

A chamada de envio tem uma estrutura previsível. Você se autentica com sua API key da HiAPI no cabeçalho Authorization, declara Content-Type: application/json e envia o corpo JSON. Conceitualmente, o corpo se parece com isto:

{
  "model": "kling-3.0-omni/image-to-video",
  "input": {
    "image": "https://your-cdn.example.com/frame-first.jpg",
    "prompt": "A slow dolly-in as morning light crosses the room"
  }
}

A estrutura importa mais que os valores literais: o identificador do modelo, o objeto de entrada que carrega a referência à imagem de origem e o prompt, e o espaço para as opções de geração. Valide contra a documentação atual antes de codificar cada chave.

A chamada curl equivalente segue o padrão habitual:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json

Manter o corpo em request.json em vez de inline torna a iteração muito mais fácil — você edita um arquivo, não uma entrada do histórico do shell.

Lendo a resposta

Um envio bem-sucedido é deliberadamente despretensioso. Espere receber um identificador de tarefa e um status indicando que foi aceito mas não concluído. Analise, imprima e salve:

curl -sS -X POST "$HIAPI_BASE_URL/v1/video/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d @request.json | tee submit.json

Verifique primeiro o código de status HTTP. 4xx significa que a requisição em si foi rejeitada — key errada, JSON malformado, opção não suportada, URL de imagem inválida. 2xx significa que a tarefa entrou no sistema e, a partir desse momento, o sinal útil vem do status da tarefa, não do código de status HTTP.

Antes de escrever qualquer código de consulta, execute um envio manual uma vez. Confirme que você consegue ver o ID da tarefa na saída. Essa ida e volta manual detecta a maioria dos erros de configuração logo cedo.

Lidando com entradas de primeiro e último frame

A imagem que você envia não é apenas uma referência — ela restringe o ponto de partida do plano. Essa distinção determina a maior parte das decisões de design nesta seção.

Somente primeiro frame

Quando você fornece uma única imagem, trate-a como o frame de abertura. O prompt então descreve o que deve acontecer a partir desse frame: movimento de câmera, ação do sujeito, mudanças de iluminação, ritmo. Como o modelo precisa criar tudo depois do primeiro frame, prompts que descrevem trajetória funcionam melhor do que prompts que descrevem uma cena estática. "Mantenha-se imóvel, está lindo assim" não dá nada ao modelo para animar; "ela se vira para a janela e a luz esquenta junto" lhe dá direção.

Adicionando um último frame

Uma entrada de último frame transforma a tarefa em um problema de interpolação. Você fornece dois pontos finais e o modelo gera o caminho entre eles. Isso é muito poderoso para transições controladas — um produto girando da vista frontal para a traseira, um personagem trocando entre duas poses — porque o destino é fixado, não imaginado.

Três observações práticas:

  • Mantenha os dois frames compatíveis. Enquadramento, perspectiva e proporção do sujeito semelhantes tornam a interpolação crível. Composições muito diferentes forçam o modelo a inventar uma ponte que pode não existir.
  • Reescreva o prompt. Com dois frames, seu texto deve descrever o movimento entre eles, não o destino. Nomear um estilo de movimento e uma duração é mais natural do que narrar o estado final que você já forneceu.
  • Antecipe mudanças na estrutura da requisição. O último frame é um campo de entrada separado, então seu corpo de requisição ganha uma chave extra. Construa o objeto de requisição de forma que campos opcionais possam ser adicionados sem reestruturar tudo.

No código, trate os frames como um pequeno conjunto de entradas nomeadas, não como uma coleção solta de URLs:

inputs = {"image": first_frame_url}
if last_frame_url:
    inputs["last_frame"] = last_frame_url

Essa condicional mantém chamadas de um e dois frames no mesmo caminho de código, o que passa a importar quando você começa a gerar variantes.

Por fim, hospede os frames em algum lugar confiável e acessível. Uma URL que expira no meio da tarefa falha após o envio, e o erro se parece com uma falha de geração, não com um problema de recurso.

Configurando áudio e resolução de saída

Áudio e resolução são escolhas no nível da requisição, não configurações globais. Você as decide por tarefa, o que significa que devem ficar no construtor do corpo da requisição, ao lado do prompt, e não em alguma constante de configuração definida uma vez e esquecida.

Áudio

As opções de áudio normalmente permitem que você solicite trilha sonora junto com o vídeo, ou a omita. Trate isso como uma decisão de modelagem relacionada ao caso de uso: um vídeo curto para redes sociais pode se beneficiar de áudio gerado, enquanto um clipe que você vai sonorizar no editor provavelmente não deve carregar áudio. Solicitar áudio que você nunca usará desperdiça tempo de geração sem nenhum benefício.

Como o comportamento do áudio é uma das áreas mais propensas a evoluir, não codifique suposições sobre o conteúdo da faixa ou sua relação com o prompt. Leia a documentação atual do modelo e depois valide a saída com uma tarefa real antes de projetar qualquer fluxo downstream em torno dela.

Resolução

Resolução mais alta significa mais pixels por frame e significativamente mais computação. Um fluxo de trabalho prático é: iterar em resolução moderada enquanto ajusta o prompt e a escolha de frames, e depois regerar a configuração vencedora na resolução final. Regerar um bom resultado é muito mais barato do que descobrir no final que seu melhor resultado foi produzido com as configurações erradas.

Alguns hábitos que vale cultivar:

  • Valide antes de enviar. Se o modelo não suporta a resolução que você está passando, você quer uma rejeição clara no envio, não uma falha confusa no nível da tarefa minutos depois.
  • Armazene as configurações junto com a tarefa. Quando você compara vários resultados, precisa saber qual resolução e escolha de áudio produziu cada um.
  • Não assuma paridade entre frames. Uma tarefa de dois frames com imagens de origem grandes pode se comportar diferente de uma tarefa de um frame; teste os dois caminhos.

Onde isso deve ficar no código

Agrupe as opções de geração em seu próprio objeto, tornando a intenção óbvia e permitindo substituir padrões em um único lugar:

options = {
    "resolution": "1080p",   # confirm supported values in the model docs
    "audio": True,
}

Adicione comentários nas linhas que dependem de valores documentados. Seu eu futuro — ou um colega — deve conseguir distinguir imediatamente quais campos são seguros e quais precisam ser verificados na página de referência antes de publicar. Em caso de dúvida, a documentação do modelo na HiAPI é a fonte da verdade, não um corpo de requisição de exemplo.

Consultando a conclusão em Python

O código de consulta é onde a maioria das integrações se torna silenciosamente frágil. O objetivo é construir um cliente pequeno e reutilizável que envia tarefas e as aguarda com limites razoáveis, em vez de um script copiado, colado e editado a cada vez.

Estrutura mínima do cliente

import os, time, requests

BASE = os.environ["HIAPI_BASE_URL"]
KEY = os.environ["HIAPI_API_KEY"]
HEADERS = {"Authorization": f"Bearer {KEY}"}

def submit(payload: dict) -> str:
    r = requests.post(f"{BASE}/v1/video/generations",
                      headers=HEADERS, json=payload, timeout=30)
    r.raise_for_status()
    return r.json()["id"]

def wait(task_id: str, interval: float = 5.0, max_wait: float = 600.0) -> dict:
    deadline = time.monotonic() + max_wait
    while time.monotonic() < deadline:
        r = requests.get(f"{BASE}/v1/video/generations/{task_id}",
                         headers=HEADERS, timeout=30)
        r.raise_for_status()
        job = r.json()
        if job["status"] in ("succeeded", "failed"):
            return job
        time.sleep(interval)
    raise TimeoutError(f"task {task_id} not finished in {max_wait}s")

Quatro detalhes nesse código carregam a maior parte do peso:

  • Timeouts explícitos em cada chamada HTTP. Uma requisição travada é pior do que uma requisição que falha.
  • Tempo máximo de espera. Um loop sem limite transforma uma tarefa travada em um serviço travado.
  • Verificação de estados terminais. Só pare quando o status for realmente final; trate todo o resto como ainda em execução, incluindo estados que você não reconhece.
  • Retornar o objeto de tarefa completo. Quem chama normalmente precisa da URL de saída e de quaisquer detalhes de erro, não apenas de um booleano.

Ritmo de consulta

Para tarefas de vídeo, cinco segundos é um padrão razoável. Consultar a cada 200ms consome cota sem nenhum benefício, já que a renderização leva minutos de qualquer forma. Se você envia muitas tarefas, coloque o loop de consulta em um worker em vez de no manipulador da requisição, e aplique um leve backoff para tarefas de longa duração.

Conectando as partes

payload = {
    "model": "kling-3.0-omni/image-to-video",
    "input": {"image": first_frame_url, "prompt": prompt},
    "options": options,
}

job = wait(submit(payload))
if job["status"] == "failed":
    raise RuntimeError(job.get("error") or "generation failed")
print(job["output"]["url"])

Como submit retorna uma string simples e wait retorna um dict, você pode testar cada metade de forma independente: simule respostas de envio para testar a lógica de consulta e chame submit isoladamente para verificar o corpo da requisição contra a API real.

Depurando falhas comuns

As falhas se concentram em três camadas, e identificar a camada é a maior parte do diagnóstico.

Camada 1: rejeições no envio

Estas aparecem como respostas 4xx antes de qualquer tarefa existir. Causas comuns:

  • Autenticação — cabeçalho ausente, token bearer malformado, key não exportada no ambiente que você pensa.
  • Estrutura do corpo — campo com erro de digitação, string onde se esperava um array, nível de aninhamento errado nas opções.
  • Valores não suportados — uma combinação de resolução ou áudio que o modelo não aceita.
  • Problemas de recurso — URL de arquivo inacessível, expirada ou que não é uma imagem.

Leia o corpo da resposta de erro. A API normalmente aponta o campo problemático, e a correção mais rápida costuma ser a sugerida pela mensagem. Reproduza a chamada que falhou em curl para confirmar que o problema está no corpo da requisição e não no código do cliente.

Camada 2: problemas de transporte

A rede fica entre seu código e o ciclo de vida da tarefa. Timeouts e resets de conexão na chamada de envio são ambíguos: a tarefa pode ter sido criada ou não. Se você reenviar cegamente, pode acabar com tarefas duplicadas.

O padrão seguro é só repetir operações seguras por idempotência e registrar cada tentativa de envio junto com sua resposta. Se você realmente não conseguir determinar se o envio teve sucesso, verifique o histórico de tarefas antes de reenviar. Na consulta, uma única falha de verificação de status não equivale a uma tarefa com falha — capture a exceção, espere e pergunte de novo.

Camada 3: falhas no nível da tarefa

Estas chegam como estado terminal de uma tarefa que havia sido aceita. Normalmente significam que o conteúdo não pôde ser gerado conforme especificado: os dois frames são diferentes demais para interpolar, o prompt não se concilia com a imagem de origem, ou houve um erro interno durante a renderização.

Distinga-as claramente no código, porque as remediações são diferentes. 4xx significa corrigir a requisição. Tarefa com falha significa alterar a entrada ou tentar de novo — se o mesmo corpo falhar duas vezes, pare de tentar e mude algo: simplifique o prompt, escolha frames mais próximos, reduza a resolução.

Checklist rápido de triagem

  1. O status HTTP é 4xx? Corrija o corpo da requisição ou as credenciais; não consulte.
  2. O envio foi bem-sucedido, mas a tarefa nunca termina? Seu limite de espera é curto demais ou a tarefa travou — registre o ID da tarefa e investigue; não faça loop infinito.
  3. A tarefa falhou? Leia os detalhes do erro e depois ajuste as entradas, em vez de reenviar igual.
  4. Sucesso, mas o vídeo está errado? Isso é problema de prompt e escolha de frames, não da API.

Publicação: checklist de produção

Antes de expor a usuários, reforce as partes que falham de forma silenciosa.

Configuração. Mantenha a base URL e a API key em variáveis de ambiente ou em um gerenciador de segredos, nunca no código-fonte. Se qualquer uma estiver faltando, falhe rápido na inicialização, e não na primeira requisição de usuário.

Timeouts e retries. Toda chamada de saída precisa de timeout explícito. Só repita envios onde a duplicação for tolerável, e trate retries de consulta como seguros — leituras de status idempotentes. Limite o tempo total de espera e exponha um timeout claro para quem chama.

O ID da tarefa é o registro. Persista o ID da tarefa junto com o corpo da requisição enviada: frames, prompt, resolução, escolha de áudio e timestamp. Quando alguém perguntar duas semanas depois por que o vídeo está errado, essa linha é a resposta completa.

A saída é temporária. Trate a URL de vídeo retornada como temporária. Assim que a tarefa tiver sucesso, baixe e armazene em seu próprio armazenamento persistente, e registre o local do armazenamento na tarefa.

Consciência de custo. A geração de vídeo é cobrada pela saída gerada, então resolução, duração e número de gerações são as alavancas que importam. Itere em resolução baixa, gere a versão final com intenção e registre as configurações para atribuir o gasto a um caso de uso, em vez de descobri-lo na fatura.

Observabilidade. Emita linhas de log no envio e no estado terminal, contendo ID da tarefa, latência e status. Uma métrica simples de taxa de sucesso por modelo mostrará quando mudanças no prompt ou ajustes de opções começarem a causar falhas nas tarefas.

Valide contra a documentação, não contra exemplos. Antes de cada publicação que envolva áudio, resolução ou entrada de frames, confirme os nomes dos campos e os valores aceitos na página de referência do modelo, pois são as configurações mais propensas a mudar.

Com o cliente, o loop de consulta e essas salvaguardas, kling-3.0-omni/image-to-video se torna um componente previsível, em vez de um script que você torce para continuar funcionando.

Artigos relacionados

Ver todos os artigos →

Comece a criar com artefactos de IA persistentes

Obtenha a sua chave de API e chame modelos de imagem, vídeo e áudio através de uma única API baseada em tasks.

Ler a documentação da API