HiAPI
Blogue

Tutorial

Como usar flux-2-klein-9b/image-to-image via API hiapi: curl, Python e uma requisição executável

Jul 30, 2026
Como usar flux-2-klein-9b/image-to-image via API hiapi: curl, Python e uma requisição executável

Este tutorial mostra como chamar o modo image-to-image do flux-2-klein-9b através da API unificada de tarefas da hiapi: a estrutura exata da requisição, um comando curl executável, um script Python pronto para colocar no backend e uma peculiaridade de schema que você encontrará se migrar de outros modelos da plataforma.

Ao final, você terá um script que recebe uma única URL de imagem de referência e um prompt de edição, e retorna uma URL de imagem gerada para download.

O que você precisa preparar

  • Uma conta hiapi e uma API key. Obtenha em console hiapi.
  • Exatamente uma imagem de referência hospedada em uma URL HTTPS pública. Diferente de alguns modelos image-to-image da plataforma que aceitam várias imagens de referência, o array image_urls do flux-2-klein-9b tem limite de um item — veja detalhes abaixo.
  • Um prompt de texto descrevendo a edição ou transformação.

Todas as requisições são enviadas ao endpoint unificado de tarefas, com a chave no cabeçalho Authorization:

POST https://api.hiapi.ai/v1/tasks
Authorization: Bearer sk-<your-key>
Content-Type: application/json

Estrutura da requisição (e um campo fácil de errar)

O id do modelo é flux-2-klein-9b/image-to-image — passe-o exatamente como está, igual ao listado em /v1/models. Seu schema de input é rigoroso:

Campo Tipo Obrigatório Descrição
prompt string Sim Instrução de edição.
image_urls string[] Sim Exatamente uma URL de imagem de referência — minItems e maxItems são ambos 1.
aspect_ratio string Não Um dos valores: 1:1, 4:3, 3:4, 16:9, 9:16.
seed integer Não Fixe-o para reproduzir resultados.
output_format string Não Um dos valores: jpeg, png, webp.

Antes de escrever qualquer código, vale saber duas coisas:

  1. image_urls aceita apenas uma URL, não um intervalo.Se você construiu código compartilhado de submissão em torno de um modelo que aceita 1–14 imagens de referência, enviar duas URLs aqui será rejeitado — maxItems: got 2, want 1. Este modelo executa edição de imagem única, não fusão de múltiplas referências.
  2. Campos extras são rejeitados, não ignorados.resolution, size, n, strength, image_size e num_images — comuns em outros modelos de imagem da plataforma — retornam aqui 400 INVALID_REQUEST: additional properties '<field>' not allowed. Não reutilize o payload de outro modelo sem verificar o schema deste.

Exemplo mínimo executável: curl

Criar a tarefa:

curl -X POST https://api.hiapi.ai/v1/tasks \
  -H "Authorization: Bearer sk-<your-key>" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "flux-2-klein-9b/image-to-image",
    "input": {
      "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
      "image_urls": ["https://your-cdn.example.com/subject.png"],
      "aspect_ratio": "3:4",
      "output_format": "png"
    }
  }'

Após submissão bem-sucedida, retorna um id de tarefa em data:

{"code": 200, "data": {"taskId": "tk-hiapi-..."}}

Consultar o resultado:

curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... \
  -H "Authorization: Bearer sk-<your-key>"

Durante a execução, data.status passa por queued → handling → archiving. Ao concluir, muda para "success" e a imagem aparece em data.output:

{
  "code": 200,
  "data": {
    "status": "success",
    "output": [{"url": "https://.../result.png", "expireAt": "..."}]
  }
}

A URL de saída tem curta duração (observe expireAt). Baixe os bytes imediatamente e armazene por conta própria — nunca faça hotlink nem persista a URL original.

Python para produção: submeter, consultar, baixar

import time
import requests

API_BASE = "https://api.hiapi.ai/v1/tasks"
API_KEY = "sk-<your-key>"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}


def edit_image(prompt: str, image_url: str,
               aspect_ratio: str = "1:1", output_format: str = "png") -> bytes:
    # 1. Criar tarefa — image_urls deste modelo aceita exatamente uma URL
    resp = requests.post(API_BASE, headers=HEADERS, json={
        "model": "flux-2-klein-9b/image-to-image",
        "input": {
            "prompt": prompt,
            "image_urls": [image_url],
            "aspect_ratio": aspect_ratio,
            "output_format": output_format,
        },
    }, timeout=60)
    body = resp.json()
    if resp.status_code != 200 or not (body.get("data") or {}).get("taskId"):
        raise RuntimeError(f"create failed [{resp.status_code}]: {body}")
    task_id = body["data"]["taskId"]

    # 2. Consultar até o estado terminal
    deadline = time.time() + 600
    while time.time() < deadline:
        task = requests.get(f"{API_BASE}/{task_id}", headers=HEADERS,
                            timeout=30).json().get("data") or {}
        if task.get("status") == "success":
            outputs = task.get("output") or []
            if not outputs or not outputs[0].get("url"):
                raise RuntimeError(f"task {task_id} succeeded but returned no output URL")
            # 3. Baixar imediatamente — a URL expira
            img = requests.get(outputs[0]["url"], timeout=120)
            img.raise_for_status()
            return img.content
        if task.get("status") == "fail":
            err = task.get("error") or {}
            raise RuntimeError(f"task failed: {err.get('code')} {err.get('message')}")
        time.sleep(5)
    raise TimeoutError(f"task {task_id} still running after 600s")


if __name__ == "__main__":
    png = edit_image(
        "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
        "https://your-cdn.example.com/subject.png",
        aspect_ratio="3:4",
    )
    with open("edited.png", "wb") as f:
        f.write(png)
    print(f"saved edited.png ({len(png)} bytes)")

Edições reprodutíveis com seed

Passe um inteiro seed e o modelo o reutilizará naquela geração, útil quando você quer manter tudo o mais igual e fazer teste A/B de redação do prompt:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": {
    "prompt": "Change the jacket to charcoal grey leather, keep the pose and background unchanged",
    "image_urls": ["https://your-cdn.example.com/subject.png"],
    "seed": 42
  }
}

Omisso, o modelo escolhe um seed aleatório a cada chamada, que é o desejado para tráfego normal de produção — quando precisar de saídas com aparência diferente, altere o prompt, não o seed.

Usar callback em vez de consulta

Para scripts e filas de workers, a consulta periódica funciona. Para backends Web, registre um callback ao criar a tarefa e deixe a hiapi enviar o estado terminal — o objeto callback fica no nível superior da requisição, ao lado de model:

{
  "model": "flux-2-klein-9b/image-to-image",
  "input": { "...": "..." },
  "callback": {
    "url": "https://your-app.example.com/hooks/hiapi",
    "when": "final"
  }
}

Com "when": "final", você recebe exatamente um POST quando a tarefa atinge sucesso ou falha. Duas regras para o handler:

  • Torne-o idempotente.Chaveie sua lógica pelo id da tarefa para que webhooks reentregues não processem duas vezes. Armazene o id da tarefa na submissão e trate o callback como “buscar e finalizar”, não como fonte única de verdade.
  • Mantenha um caminho de reconciliação.Se seu endpoint estiver indisponível quando o callback disparar, varra periodicamente ids não resolvidos e faça GET em /v1/tasks/<id> para capturar qualquer perda.

Se seu callback parece nunca chegar, investigue uma a uma por que seu callback de tarefa hiapi não está disparando — o texto cobre os modos de falha comuns por ordem de probabilidade.

Tratamento de erros: três cenários críticos de falha

401 permission_denied na criação — sua chave não pode usar este modelo. Corpo da resposta assim:

{"error": {"code": "permission_denied", "message": "...", "request_id": "...", "type": "hiapi_error"}}

Se ocorrer, investigue API key inválida, ou verifique as permissões de modelo da chave no console.

400 INVALID_REQUEST na criação — seu input não corresponde ao schema deste modelo. A mensagem aponta diretamente o campo infrator (por exemplo, <root>: additional properties 'resolution' not allowed, ou image_urls: maxItems: got 2, want 1). São erros permanentes — repetir o mesmo payload falhará para sempre; corrija o campo e reenvie.

Tarefa retorna status: "fail" — criação bem-sucedida, mas geração falhou (URL de referência inacessível ao backend, política de conteúdo, capacidade upstream). O error.code e error.message da tarefa indicam qual. Diferente do 400, alguns são transitórios e vale tentar novamente uma vez com backoff.

Vale saber também: GET em um id de tarefa inexistente ou expirado retorna 404 task not found — em varreduras de reconciliação, trate isso como estado terminal, não como “ainda em espera”.

Para onde ir depois

Perguntas frequentes

Posso passar várias imagens de referência para flux-2-klein-9b image-to-image? Não. minItems e maxItems de image_urls são ambos 1 — exatamente uma URL, nem mais nem menos. Se precisar de fusão de múltiplas referências, veja modelos cujo schema documenta intervalo maior, como seedream-5.0-lite.

Por que recebo “additional properties 'resolution' not allowed”? O schema de input da API de tarefas é por modelo. flux-2-klein-9b/image-to-image não aceita campos resolution ou size — o tamanho de saída segue a imagem de entrada e aspect_ratio. Remova qualquer campo de dimensão trazido de payload de outro modelo.

Posso enviar a imagem de referência em base64 em vez de URL? O schema deste modelo aceita image_urls — um array de URL. Hospede a imagem em URL HTTPS acessível (qualquer CDN, bucket de object storage ou URL pré-assinada serve) e passe a URL.

O que seed realmente controla? Mesmo seed, mesmo prompt e mesma imagem de referência reproduzem o mesmo resultado, útil para teste A/B controlado de prompt. Em tráfego normal omita-o, para cada chamada ter seu próprio seed aleatório.

O mesmo código serve para flux-2-klein-9b text-to-image? Endpoint e fluxo são iguais, mas text-to-image tem seu próprio schema de input (sem image_urls). Troque o id do modelo para flux-2-klein-9b/text-to-image e remova a imagem de referência — depois valide com uma chamada de teste, pois os campos obrigatórios variam por modo.

Quanto custa por imagem? A cobrança é por uso a cada geração. Veja tarifas em tempo real na página de preços.

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