HiAPI
Blogue

Tutorial

Como chamar flux-2-klein-4b/text-to-image pela API HiAPI: curl, Python e uma requisição que funciona

Um tutorial prático para integrar o nível econômico de imagens ao seu pipeline

Oct 31, 2026
平面玻璃风格封面图:数层半透明玻璃斜向叠压,把浅灰底场折射成冷蓝色带,构图安静克制。

Por que flux-2-klein-4b/text-to-image é adequado para cargas de trabalho sensíveis a custo

Nem todo pipeline de imagens precisa do modelo mais pesado do catálogo. Uma parte considerável do tráfego em produção é repetitiva: imagens de produtos, prévias de variantes, miniaturas de listagens de produtos, cards sociais gerados a partir do mesmo template. Para esses cenários, flux-2-klein-4b/text-to-image foi projetado como um nível deliberadamente mais leve — você troca um pouco da mais alta fidelidade por throughput e previsibilidade, e isso costuma ser a escolha correta ao gerar milhares de assets em lote de acordo com um cronograma.

Dois tipos de carga de trabalho se beneficiam mais:

  • Imagens de produtos de e-commerce. Você normalmente tem uma descrição-base limpa ("caneca de cerâmica fosca, 350ml, cinza-quente, sobre fundo branco sem emendas") e precisa gerar muitas variantes renderizadas cobrindo cores, ângulos e composição. Consistência importa mais que expressividade artística.
  • Geração em lote para interfaces internas ou de cauda longa. Banners de categoria, criativos de testes A/B, imagens de placeholder para listagens em rascunho — nesses assets, o ganho marginal de qualidade não vale o custo de execução mais pesado.

Quando você está produzindo imagens principais, texturas detalhadas e quando a composição em si é o produto, escolha o nível mais pesado. Quando volume de geração, latência e custo por imagem dominam a decisão, escolha flux-2-klein-4b/text-to-image. O restante deste tutorial usa esse nível de ponta a ponta: estrutura da requisição, chamada curl, função auxiliar em Python, estratégia de proporção e ciclo de vida assíncrono.

Preparação antes de enviar a requisição

Antes da primeira chamada, é preciso definir três coisas.

1. Chave de API do HiAPI

Crie a chave no console do HiAPI. Toda requisição descrita aqui é autenticada com essa chave, e ela é a única credencial necessária — não há um processo separado de registro por modelo.

2. Manter a chave fora do controle de código-fonte

Trate a chave como qualquer outro segredo. Uma prática que funciona bem:

export HIAPI_API_KEY="your-key-here"

Depois leia-a do ambiente no shell e no Python. Nunca a faça commit, e se você usa um carregador de dotfile, adicione .env ao .gitignore. Se a chave vazar, rotacione-a no console em vez de tentar limpar o histórico.

3. Confirmar o identificador do modelo

A string do identificador é usada literalmente no corpo da requisição e, em algumas configurações, também na URL:

flux-2-klein-4b/text-to-image

Copie exatamente — tanto 4b quanto o sufixo /text-to-image fazem parte do nome. Erros de digitação aqui são a causa mais comum de 404 ou erros de "unknown model". Assim que a chave estiver no ambiente e o identificador confirmado, você pode montar o payload.

Composição de uma requisição flux-2-klein-4b/text-to-image

As requisições de texto para imagem no HiAPI são deliberadamente enxutas. Você fornece o identificador do modelo e um payload de parâmetros de geração; o retorno é o resultado ou um handle de job, dependendo de como o endpoint desse modelo está configurado.

Os campos mais importantes

  • model — o identificador exato, flux-2-klein-4b/text-to-image.
  • prompt — uma string que descreve a imagem. Este é o campo que mais influencia a qualidade da saída; concentre esforço nele antes de mexer em qualquer outra coisa.
  • Tamanho ou proporção — como você especifica o enquadramento da saída. Prefira especificações na forma de proporção em vez de pares de pixels fixos, porque isso permanece estável mesmo que o comportamento de resolução subjacente mude.

Como o retorno se parece

O retorno assume duas formas, e seu cliente deve lidar com ambas:

  1. Payload síncrono — a resposta contém diretamente a localização da imagem gerada (uma URL).
  2. Payload de job assíncrono — a resposta contém um identificador de job e um status (pending, processing, succeeded, failed são os estados típicos). Você então usa esse identificador para consultar o endpoint de status até que ele atinja um estado terminal e o payload traga a localização da saída.

Não invente parâmetros que você não pode verificar. Se um campo não faça parte do conteúdo documentado de requisição do modelo, não o inclua — campos desconhecidos são a segunda causa mais comum de erros 400 confusos. Comece com model, prompt e proporção para obter uma imagem funcional e depois adicione parâmetros opcionais um a um, verificando a saída após cada alteração.

Sua primeira chamada curl funcional

Abaixo está uma requisição completa que você pode colar no terminal. Ela assume que HIAPI_API_KEY foi exportada conforme descrito acima.

curl -sS -X POST "https://api.hiapi.ai/v1/images/generations" \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-2-klein-4b/text-to-image",
    "prompt": "Studio product photo of a matte ceramic mug, warm grey, soft diffused light, seamless white background, centered composition",
    "aspect_ratio": "1:1"
  }'

Passe a saída por um formatador JSON (| jq .) para ler a resposta. Você verá diretamente a URL da imagem ou um objeto de job com id e status.

Interpretando o resultado

  • Se você receber uma URL de imagem, abra-a no navegador. Isso confirma que a chave, o identificador do modelo e o prompt estão funcionando.
  • Se você receber um objeto de job, anote o id — você o consultará nas seções seguintes.

Diagnosticando falhas comuns

  • 401 / 403 — chave ausente, mal formatada ou não enviada no cabeçalho Authorization: Bearer. Verifique se a variável de ambiente está realmente exportada no shell onde o curl é executado.
  • 404 ou "unknown model" — identificador errado. Ele deve ser exatamente flux-2-klein-4b/text-to-image.
  • 400 com mensagem de validação — geralmente falta prompt, nome de parâmetro não suportado ou proporção fora do conjunto aceito. Reduza o payload aos três campos principais e tente novamente.
  • 429 — você está enviando requisições mais rápido do que seu nível permite. Adicione backoff em vez de tentar novamente em loop apertado.

Quando o curl funcionar, você terá uma requisição comprovadamente funcional para comparar caso a versão em Python apresente problemas.

Portando a mesma requisição para Python

A versão em Python é um invólucro fino em torno do mesmo payload. Organize-a como funções para que o restante do pipeline possa chamá-la sem repetir a lógica HTTP.

import os
import time
import requests

API_BASE = "https://api.hiapi.ai/v1"
MODEL = "flux-2-klein-4b/text-to-image"

def generate_image(prompt: str, aspect_ratio: str = "1:1") -> dict:
    """Submit a text-to-image job and return the parsed response."""
    api_key = os.environ["HIAPI_API_KEY"]
    payload = {
        "model": MODEL,
        "prompt": prompt,
        "aspect_ratio": aspect_ratio,
    }
    response = requests.post(
        f"{API_BASE}/images/generations",
        headers={
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        },
        json=payload,
        timeout=60,
    )
    if response.status_code >= 400:
        # Surface the server's message rather than a bare status code.
        raise RuntimeError(f"HiAPI error {response.status_code}: {response.text}")
    return response.json()

Alguns hábitos que dão resultado imediato:

  • Leia a chave do ambiente dentro da função, e não no momento da importação, para que o mesmo módulo funcione em notebooks, scripts e tarefas agendadas.
  • Verifique explicitamente os códigos de status e inclua o corpo da resposta na exceção. As mensagens de validação do HiAPI informam qual campo está errado; um raise_for_status() puro esconde isso.
  • Defina timeouts. Sem timeouts, uma conexão travada pode deixar um worker pendurado indefinidamente.
  • Mantenha o payload como dict, para que você possa registrá-lo, compará-lo em diff e estender parâmetros opcionais sem reescrever a formatação de string.

Agora um driver mínimo:

result = generate_image(
    "Studio product photo of a matte ceramic mug, warm grey, "
    "soft diffused light, seamless white background, centered composition"
)
print(result)

Escolhendo proporções que resistem à produção

A proporção é o parâmetro com maior probabilidade de gerar retrabalho mais tarde, porque os sistemas downstream assumem enquadramentos. Decida superfície por superfície, não requisição por requisição.

Mapeamento prático

  • Quadrado (1:1) — listagens de marketplace, grades de produtos, avatares e posições de miniatura. É o padrão seguro para e-commerce, porque a maioria dos tiles de produto é quadrada e o recorte funciona mal.
  • Vertical (3:4 ou 4:5) — posições de feed social e imagens de produto no estilo lookbook. Dá espaço para produtos verticais como garrafas e roupas sem adicionar tarjas pretas.
  • Horizontal (16:9) — banners, faixas de hero, cabeçalhos de e-mail, miniaturas de vídeo.

Por que fixar a proporção a montante

Se a proporção for escolhida no momento da renderização pelo código que por acaso chama a API, você acabará com um catálogo de produtos em que metade dos tiles é quadrada e metade é vertical, e precisará pagar por uma regeneração para corrigir. Em vez disso, codifique a proporção no seu modelo de conteúdo — um campo surface ou placement que mapeia deterministicamente para uma proporção — e gere a partir desse campo. Use 1:1 em posições que toleram recorte; use a proporção exigida pelo layout em posições com layout fixo.

A mesma disciplina se aplica aos prompts: inclua dicas de enquadramento ("composição centralizada", "produto inteiro dentro do quadro", "espaço em branco à esquerda para texto") no template de prompt, e não em strings ad hoc por chamada. Templates fazem mil imagens geradas parecerem saídas do mesmo pipeline, e é exatamente isso que um catálogo de produtos precisa.

Uma ressalva: nem todo modelo aceita toda proporção. Envie a proporção escolhida uma vez, confirme o sucesso e só então a aplique ao lote inteiro.

Consultando jobs assíncronos até a conclusão

Quando flux-2-klein-4b/text-to-image retorna um handle de job em vez de uma imagem, trate-o como uma máquina de estados. O fluxo é: enviar → receber o ID do job → consultar o status → estado terminal → ler a saída.

def wait_for_job(job_id: str, timeout_s: int = 300, interval_s: int = 2) -> dict:
    api_key = os.environ["HIAPI_API_KEY"]
    deadline = time.time() + timeout_s
    while time.time() < deadline:
        response = requests.get(
            f"{API_BASE}/jobs/{job_id}",
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=30,
        )
        response.raise_for_status()
        job = response.json()
        status = job.get("status")
        if status == "succeeded":
            return job
        if status == "failed":
            raise RuntimeError(f"Job {job_id} failed: {job.get('error')}")
        time.sleep(interval_s)
    raise TimeoutError(f"Job {job_id} did not finish in {timeout_s}s")

Tratando cada estado

  • pending / processing — continue aguardando. Comece com intervalos curtos e considere backoff suave (por exemplo, aumentando o sono até um limite) para não martelar o endpoint de status durante filas longas.
  • succeeded — o payload agora contém a localização da saída. Pare de consultar imediatamente.
  • failed — não tente novamente às cegas. Registre a mensagem de erro; se for um problema de prompt ou parâmetro, reenviar a mesma requisição falhará da mesma forma.

Sabendo quando parar

Sempre limite o loop com um prazo de relógio de parede. Um worker que consulta para sempre é um worker vazando. Em caso de timeout, exponha o ID do job para inspeção manual — se o job succeed depois, você ainda poderá recuperar a saída. Para lotes, execute vários jobs concurrentemente e consulte todos juntos, em vez de serializar envio e espera para cada imagem.

Baixando e armazenando a saída

A imagem gerada aparece na resposta de geração ou de job como uma localização recuperável. Busque-a no servidor e persista-a por conta própria — não faça hotlink da URL retornada em páginas de produto, pois ela é um artefato de entrega, não sua camada de armazenamento.

def download_image(url: str, path: str) -> str:
    response = requests.get(url, timeout=120)
    response.raise_for_status()
    with open(path, "wb") as f:
        f.write(response.content)
    return path

Usando um esquema de nomes previsível

Caminhos como images/{surface}/{sku}/{variant}-{ratio}.png dão três coisas: os assets ficam naturalmente agrupados por produto, a superfície informa qual enquadramento foi solicitado, e a proporção no nome do arquivo torna duplicatas inesperadas óbvias. Se você regenerar variantes do mesmo SKU, inclua um hash curto do prompt no nome do arquivo — assim uma reexecução não sobrescreve silenciosamente uma tentativa anterior que você talvez quisesse comparar.

Fechando o ciclo

Registre o ID do job junto com o arquivo armazenado, em um manifesto, linha de banco de dados ou arquivo sidecar. Quando um sistema downstream reportar uma imagem ruim, o ID do job é o que permite recuperar o prompt e os parâmetros originais. Combinado com nomes de arquivo endereçados por conteúdo, isso fornece uma trilha de auditoria reproduzível do asset armazenado até a requisição exata que o produziu.

Com envio, consulta e download conectados, flux-2-klein-4b/text-to-image se torna uma etapa comum no seu pipeline: enfileirar trabalho, consultar com prazo, armazenar com nomes estáveis. A partir daí, escalar é apenas uma questão de concorrência e templates, não de novo código.

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