Tutorial
Como chamar a API Imagen 4 via HiAPI: exemplos em curl e Python
O que o endpoint do Imagen 4 oferece
Se você já tem um prompt e quer obter uma imagem sem registrar uma conta de faturamento, sem montar uma fila e sem criar um bucket em um provedor que você nunca usou, o modelo Google-Imagen4 na HiAPI é um ponto de partida razoável. Você envia um prompt de texto para um único endpoint HTTP, autentica com uma chave de API e recebe uma referência à imagem gerada (ou uma tarefa que pode ser consultada por polling). Todo o resto — hospedagem do modelo, capacidade, novas tentativas do lado do provedor — fica abstraído.
As chamadas de texto para imagem por esse endpoint cobrem três coisas que você realmente vai se importar no uso diário:
- Do prompt à imagem. O prompt é a entrada principal. Dependendo de como você configura a requisição, o modelo retorna uma ou mais imagens.
- Controle do formato de saída. Você pode especificar a proporção (quadrada, paisagem, retrato) na hora da requisição, em vez de cortar depois. Isso é mais importante do que parece: cortar uma imagem já gerada descarta a composição que o modelo já havia planejado.
- Execução assíncrona. Gerações mais longas são tratadas como tarefas. Você envia, consulta o status e obtém o resultado. Chamadas curtas podem retornar rápido, mas você deve escrever o cliente como se toda chamada fosse assíncrona, porque é esse o fluxo que aguenta tráfego de produção.
O restante deste tutorial constrói a mesma requisição de três maneiras: uma chamada curl crua que você pode colar no terminal, um cliente Python equivalente e, em seguida, as partes de que você precisa ao lidar com proporção, polling, persistência e tratamento de erros. Se você quer gerar imagens com um comando de shell de uma linha, isso serve. Se você precisa de controle granular sobre máscaras de inpainting ou loops de treinamento, talvez não seja a melhor escolha.
Dois pré-requisitos antes de começar: você tem uma chave de API da HiAPI e seu ambiente de execução consegue fazer requisições HTTPS de saída. Nada além disso é obrigatório — instalar SDK não é necessário, embora os exemplos em Python abaixo usem requests.
Faça sua primeira requisição com curl
A forma mais rápida de confirmar que a chave funciona é uma chamada curl. O endpoint recebe um corpo JSON, então você precisa de dois cabeçalhos: Content-Type para o corpo e Authorization para a chave. Leia a chave a partir de uma variável de ambiente em vez de colá-la diretamente no comando, para que ela não fique no histórico do seu shell.
export HIAPI_API_KEY="your-key-here"
curl -sS -X POST "https://api.hiapi.ai/v1/images/generations" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-d '{
"model": "google-imagen-4",
"prompt": "a weathered lighthouse on a basalt cliff, morning fog, wide shot",
"n": 1,
"aspect_ratio": "16:9"
}'
Alguns pontos deste corpo merecem atenção:
modelseleciona o Google-Imagen4. Escreva-o explicitamente mesmo que o serviço tenha um padrão — isso mantém seu código legível daqui a um ano.prompté a única entrada criativa obrigatória. Escreva-o como você descreveria uma cena para um fotógrafo: sujeito, ambiente, iluminação, enquadramento.ncontrola quantas imagens você quer. Comece com uma enquanto acerta a estrutura da resposta; aumente depois que confiar no seu pipeline.aspect_ratioé detalhado mais adiante. Omiti-lo recorre ao padrão do serviço.
Uma resposta bem-sucedida retorna um objeto JSON. A parte que importa é a lista de entradas de imagem; cada entrada traz uma referência para download ou uma URL que você pode entregar ao frontend. Se você pediu várias imagens, recebe várias entradas, na ordem da requisição. Se a geração demorar o suficiente para ser enfileirada, a resposta em vez disso traz um identificador e um status consultável — esse fluxo é descrito em sua própria seção abaixo.
Se você receber um 4xx, verifique primeiro os dois cabeçalhos: faltar o prefixo Bearer , ou a chave terminar com espaço em branco, é a causa da maioria das falhas na primeira chamada. Use -i durante o debug para ver a linha de status e o corpo do erro.
Faça a mesma requisição em Python
Em um script, envolva a chamada em uma função pequena, para que o restante do código nunca precise tocar em HTTP cru. A função deve receber o prompt e os parâmetros opcionais, montar o corpo, enviar a requisição e retornar o JSON analisado.
import os
import requests
HIAPI_BASE = "https://api.hiapi.ai/v1"
def generate_image(prompt: str, aspect_ratio: str = "1:1", n: int = 1) -> dict:
response = requests.post(
f"{HIAPI_BASE}/images/generations",
headers={
"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
"Content-Type": "application/json",
},
json={
"model": "google-imagen-4",
"prompt": prompt,
"n": n,
"aspect_ratio": aspect_ratio,
},
timeout=120,
)
response.raise_for_status()
return response.json()
if __name__ == "__main__":
result = generate_image(
"a weathered lighthouse on a basalt cliff, morning fog, wide shot",
aspect_ratio="16:9",
)
print(result)
Três escolhas de design neste código valem a pena manter quando você o adaptar:
Timeout explícito. requests não tem timeout padrão, o que significa que uma conexão travada pode deixar um worker suspenso indefinidamente. Escolha um valor acima do seu tempo de geração mais lento esperado e faça-o falhar explicitamente.
Chame raise_for_status() antes de analisar. Respostas de erro nem sempre compartilham o mesmo schema das respostas de sucesso. Verifique o status HTTP antes de tocar no corpo da resposta.
Função pequena e parametrizada. Quando você adicionar polling e download, essa função continua sendo o único ponto de entrada para o envio, com a lógica de ciclo de vida ao lado dela, em vez de emaranhada dentro dela.
O resultado analisado contém a mesma informação da saída do curl. Para respostas síncronas, percorra as entradas de imagem e extraia a URL. Para respostas assíncronas, extraia o identificador da tarefa e passe-o para a função de polling. Seu código deve ramificar conforme a forma recebida, em vez de assumir que existe apenas uma.
Controle a proporção e a saída
A proporção é o parâmetro que você mais vai ajustar, e vale configurá-lo corretamente em vez de cortar depois. Se você pedir uma imagem quadrada e depois recortar uma faixa 16:9 dela, perde o topo e a base que o modelo compôs de propósito. Pedir 16:9 antecipadamente permite que o modelo planeje o enquadramento para esse formato.
Passe-a no corpo junto com o prompt:
result = generate_image(
"a sprawling night market, neon signs, steam from food stalls",
aspect_ratio="16:9",
n=2,
)
Proporções horizontais servem para banners hero, miniaturas de cards largos e fotografia de produto; proporções verticais servem para cards mobile-first, pôsteres e retratos de personagem; proporções quadradas servem para avatares e posts sociais cujo recorte é imprevisível. Decida pelo lugar onde a imagem vai ficar, não por como ela parece isolada.
O que muda na resposta ao alterar a proporção: as dimensões em pixels das imagens retornadas. A quantidade de entradas, a ordem e o fluxo de download permanecem iguais, então seu código downstream não precisa ramificar por proporção. Se sua UI reserva espaço antes de a imagem carregar, leia as dimensões da resposta e defina-as no elemento, em vez de codificar valores fixos.
Dois outros ajustes de saída valem conhecer:
- Quantidade de imagens (
n). Mais imagens por requisição significa mais opções, mas também mais tokens gastos em um prompt que pode estar errado. Itere no texto do prompt pedindo uma única imagem; faça requisições em lote depois que o prompt estiver estável. - Texto do prompt para enquadramento. A proporção define o formato; o prompt define a composição dentro dele. Palavras como "wide shot", "close-up" e "centered" fazem o trabalho real junto com o parâmetro de proporção.
O faturamento desse endpoint acompanha a própria geração de imagens — consulte o painel de preços no site para as tarifas atuais, em vez de codificar valores fixos no seu modelo de custos.
Lide com tarefas assíncronas e polling
Quando uma geração demora o suficiente para ser enfileirada, o endpoint retorna antes de a imagem existir. Você recebe um identificador de tarefa, e seu trabalho é consultá-la até chegar a um estado terminal. Escreva esse fluxo mesmo que as chamadas de hoje retornem rápido — capacidade, complexidade do prompt e quantidade de imagens afetam a latência, e um cliente construído para o cenário síncrono quebra na primeira requisição lenta.
O ciclo de vida tem três etapas:
- Envio. Envie a requisição de geração normalmente. Se a resposta for uma tarefa, extraia seu identificador.
- Polling. Com intervalos fixos, chame o endpoint de status da tarefa com esse identificador.
- Obtenção. Assim que o status indicar sucesso, leia as referências das imagens no payload de resultado.
import time
def wait_for_job(job_id: str, interval: float = 2.0, max_attempts: int = 60) -> dict:
for attempt in range(max_attempts):
response = requests.get(
f"{HIAPI_BASE}/images/jobs/{job_id}",
headers={"Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}"},
timeout=30,
)
response.raise_for_status()
payload = response.json()
status = payload.get("status")
if status == "succeeded":
return payload
if status in {"failed", "cancelled"}:
raise RuntimeError(f"job {job_id} ended with status {status}")
time.sleep(interval)
raise TimeoutError(f"job {job_id} did not finish in time")
Detalhes que tornam esse código seguro em produção:
- Limite as tentativas, em vez de fazer um loop infinito. Uma tarefa que nunca termina deve se manifestar como um erro de timeout que dispara alerta, não um worker suspenso para sempre.
max_attemptsvezesintervalé seu orçamento real de tempo — defina-o de acordo com o seu tempo de geração mais lento realista. - Backoff sob carga. Um intervalo fixo de dois segundos serve para uso interativo. Para cargas em lote, faça backoff progressivo ou adicione jitter, para que as retentativas de muitos workers não coincidam no mesmo segundo.
- Distinga falha de desconhecido. Apenas
failedecancelledsão falhas terminais. Trate qualquer outro estado que não seja sucesso como "ainda processando" e consulte novamente; criar um ramo que lança exceção imediatamente para estados irreconhecíveis quebra quando o serviço introduz um novo estado. - Em caso de erro de rede, repita o envio, não o polling. Se a própria chamada de envio expirar, repeti-la pode gerar tarefas duplicadas. Se a API suportar chaves de idempotência, atribua uma a cada envio; caso contrário, deduplique do seu lado com um hash do prompt mais um timestamp.
Assim que uma tarefa reportar sucesso, pare o polling. Persistir o resultado e guardar o identificador da tarefa para auditoria é mais barato do que continuar perguntando por um estado que você já conhece.
Baixe e armazene as imagens
A resposta de geração fornece referências, não os bytes da imagem. Na maioria dos casos, cada entrada de imagem traz uma URL que pode ser buscada com um GET comum. Trate essa URL como um endereço temporário: baixe logo, valide e armazene os bytes onde você realmente tem controle.
import pathlib
import uuid
def download_images(payload: dict, out_dir: str = "images") -> list[pathlib.Path]:
target = pathlib.Path(out_dir)
target.mkdir(parents=True, exist_ok=True)
saved = []
for entry in payload.get("data", []):
url = entry["url"]
resp = requests.get(url, timeout=60)
resp.raise_for_status()
content_type = resp.headers.get("Content-Type", "")
if not content_type.startswith("image/"):
raise ValueError(f"unexpected content type: {content_type}")
path = target / f"{uuid.uuid4().hex}.png"
path.write_bytes(resp.content)
saved.append(path)
return saved
Faça algumas verificações antes de considerar a tarefa concluída:
- Tipo de conteúdo e tamanho. Em alguns ambientes, um redirecionamento para uma página de erro retorna 200 com HTML. Antes de gravar no armazenamento permanente, verifique se o
Content-Typecomeça comimage/e se o corpo da resposta não é minúsculo. - Nomes. Gere um nome novo a cada download, em vez de derivá-lo do prompt. Prompts se repetem, colidem e contêm caracteres que o sistema de arquivos rejeita.
- Armazene o prompt junto com o arquivo. Salve o prompt, os parâmetros e o identificador da tarefa no seu próprio banco de dados ou em um arquivo de metadados adjacente. Seis meses depois, a imagem sozinha não vai te dizer qual requisição a produziu.
- Mova para o armazenamento permanente, em vez de copiar. Grave primeiro em um local temporário, valide e depois mova para o seu bucket ou armazenamento apoiado por CDN. Um arquivo escrito pela metade no armazenamento permanente é pior do que uma tarefa que falhou.
Se seu pipeline gerar muitas imagens, separe a etapa de download da etapa de geração. A geração é a parte cara e limitada por taxa; repetir o download é barato e nunca deve fazer você gerar de novo.
Erros comuns e checklist de depuração
A maioria das falhas do primeiro dia se encaixa em poucas categorias. Percorra estas verificações em ordem — cada uma é mais rápida que a seguinte.
Falhas de autenticação (401, 403)
Chave errada, ausente ou mal formatada. Verifique se o valor do cabeçalho é exatamente Bearer seguido da chave, sem quebras de linha vindas de um copiar e colar, e confirme que a variável de ambiente está de fato exportada no shell ou processo que executa o código. Chaves rotacionadas no dashboard invalidam as antigas imediatamente — se outro serviço começou a falhar de repente, pergunte se alguém rotacionou a chave.
Parâmetros rejeitados (400, 422)
O corpo não corresponde ao schema. Causas comuns: identificador de model escrito incorretamente, string de aspect_ratio em formato não aceito pelo endpoint, ou n fora do intervalo suportado. Imprima o corpo do erro — ele geralmente aponta o campo problemático. Compare seu JSON com o exemplo curl que funciona, campo por campo.
Timeouts
Dois tipos diferentes de timeout são fáceis de confundir. Um timeout do cliente significa que sua chamada HTTP desistiu; a tarefa pode continuar rodando. Uma tarefa que nunca atinge um estado terminal significa que o orçamento de polling é pequeno demais, ou que a requisição de fato falhou. Defina o timeout do cliente acima do seu tempo de geração mais lento esperado e o orçamento de polling ainda mais alto. Quando o timeout do cliente dispara durante o envio, repetir às cegas pode duplicar trabalho — verifique se já existe uma tarefa antes de reenviar.
Downloads que parecem bem-sucedidos mas não são
Um status 200 com corpo HTML, ou um arquivo de zero bytes, geralmente significa que a URL da imagem expirou ou que a busca foi feita sem a permissão de acesso correta. Baixe imediatamente após o sucesso da tarefa e valide o tipo de conteúdo e o tamanho como descrito acima.
Limites de taxa (429)
Faça backoff e repita, em vez de apertar o loop. Backoff exponencial com jitter é a resposta padrão; limite o número de tentativas e exponha falhas persistentes ao seu monitoramento.
Um hábito de depuração que vale cultivar
Registre quatro coisas para cada requisição: o identificador do modelo, o corpo completo da requisição sem a chave, o identificador da tarefa (ou o status da resposta, no caso de chamadas síncronas) e o tempo decorrido. Quando algo quebrar em produção, esses quatro campos geralmente permitem, em menos de um minuto, determinar se o problema está no seu cliente, nos seus parâmetros ou na fila.
Comece pela chamada curl, obtenha uma imagem primeiro, depois mova a lógica para um cliente Python e adicione polling e persistência ao redor dela. Esse caminho mantém cada falha pequena e atribuível.
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