Tutorial
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_urlsdo 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:
image_urlsaceita 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.- Campos extras são rejeitados, não ignorados.
resolution,size,n,strength,image_sizeenum_images— comuns em outros modelos de imagem da plataforma — retornam aqui400 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
- página do modelo flux-2-klein-9b/image-to-image — playground e preço por imagem.
- preços hiapi — tarifas atuais de todos os modelos de imagem.
- tutorial image-to-image do seedream-5.0-lite — mesma API de tarefas, schema aceita até 14 imagens de referência em vez de 1; bom exemplo de por que payloads não são transferíveis entre modelos.
- documentação da API de tarefas assíncronas hiapi — referência completa de criar/consultar/callback.
- documentação de autenticação hiapi — configuração de API key e formato do cabeçalho.
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 →-
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