HiAPI
Blogue

Tutorial

Como usar o nano-banana pela API do HiAPI: curl, Python e requisições executáveis

O fluxo prático completo, da primeira requisição ao download do resultado

Oct 31, 2026
博客封面图:错位叠放的骨白色纸张,炭灰边缘,夹一张锈红色纸片,扁平投影。

Como é uma requisição ao nano-banana

Antes de escrever código, vale a pena entender o ciclo de vida de uma chamada ao nano-banana no HiAPI. Toda requisição segue o mesmo padrão: você envia um corpo JSON descrevendo o que deseja, o HiAPI aceita a tarefa e então você recebe de volta o resultado já concluído ou um identificador de tarefa para consultar o status de conclusão.

Algumas características definem seu formato:

  • Um endpoint, uma chave de API. Todo o tráfego do nano-banana passa pela interface unificada do HiAPI. Você se autentica com a chave do HiAPI e nunca acessa o serviço upstream diretamente.
  • JSON entra, JSON sai. O corpo da requisição é JSON. A resposta também é JSON, contendo o resultado inline ou um id de tarefa mais um campo de status.
  • Texto obrigatório, imagem opcional. No mínimo, você envia um prompt. Você pode adicionar imagens de referência para orientar o sujeito, o estilo ou o personagem.
  • Compatível com assincronismo. A geração de imagens pode levar mais tempo do que um timeout HTTP típico, por isso a API suporta tarefas assíncronas que você pode consultar até que sejam concluídas.

Estrutura aproximada do corpo da requisição:

{
  "model": "nano-banana",
  "prompt": "a description of the image you want",
  "image_input": ["https://.../reference.jpg"],
  "consistency": "character",
  "async": true
}

Resposta no modo assíncrono:

{
  "id": "job_abc123",
  "status": "queued",
  "created_at": "..."
}

Quando a tarefa termina e você a consulta, o status se torna succeeded (ou failed) e a carga do resultado traz a URL de saída. Guarde bem esse modelo de duas fases, porque tudo o mais neste tutorial se baseia nele.

Uma requisição curl executável

Esta é a menor requisição capaz de gerar uma imagem. Ela presume que HIAPI_API_KEY já foi exportada no seu shell:

curl -X POST https://api.hiapi.ai/v1/images/generations \
  -H "Authorization: Bearer $HIAPI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "nano-banana",
    "prompt": "a cozy reading nook by a rain-streaked window, warm lamp light, watercolor style",
    "async": true
  }'

Campo a campo:

  • model — defina como nano-banana. O HiAPI usa esse campo para rotear a requisição.
  • prompt — o único campo criativo estritamente obrigatório. Descreva com especificidade o sujeito, a composição e o estilo; prompts vagos são a causa mais comum de resultados decepcionantes.
  • async — quando true, a API retorna imediatamente um identificador de tarefa. Se você preferir bloquear até a conclusão, defina como false, mas prepare-se para tempos de resposta mais longos.
  • image_input — array opcional de URLs de imagens de referência. Omita-o para geração apenas a partir de texto.
  • consistency — controle opcional de consistência de personagem ou sujeito entre imagens; detalhado abaixo.

Se você não tiver certeza de quais parâmetros opcionais a versão atual suporta, consulte a página de documentação do modelo no HiAPI antes de adicionar campos — campos desconhecidos podem ser ignorados ou rejeitados dependendo do parâmetro.

Execute o comando e examine o JSON. Supondo que async seja true, você receberá de volta um objeto de tarefa em vez de uma imagem. Guarde o id; ele é o identificador da sua próxima etapa.

Portando a requisição para Python

A chamada curl pode ser traduzida diretamente. O objetivo aqui não é apenas escrever um script descartável, mas um encapsulamento reutilizável que possa entrar em um pipeline.

import os
import time
import requests

BASE = "https://api.hiapi.ai"
HEADERS = {
    "Authorization": f"Bearer {os.environ['HIAPI_API_KEY']}",
    "Content-Type": "application/json",
}

def create_image(prompt, image_input=None, consistency=None, async_job=True):
    body = {"model": "nano-banana", "prompt": prompt, "async": async_job}
    if image_input:
        body["image_input"] = image_input
    if consistency:
        body["consistency"] = consistency

    resp = requests.post(
        f"{BASE}/v1/images/generations",
        headers=HEADERS,
        json=body,
        timeout=60,
    )
    if resp.status_code >= 400:
        raise RuntimeError(f"create failed: {resp.status_code} {resp.text}")
    return resp.json()

Estes pontos a tornam robusta, e não apenas funcional:

  • Timeout explícito. Sempre passe um; o padrão de "esperar para sempre" transforma um problema transitório de rede em um processo travado.
  • Verifique o status antes de analisar. Lance uma exceção em respostas de erro, para que uma requisição ruim não retorne silenciosamente um corpo JSON que você depois tentará indexar.
  • Omita campos opcionais em vez de preenchê-los com nulo. Em vez de enviar "image_input": null, adicione a chave somente quando houver valor. Isso deixa os logs mais legíveis e evita a ambiguidade de null significar "nenhum" ou "não definido".

Encapsular em uma função também facilita novas tentativas: como a função lança exceção em caso de falha, o chamador pode envolvê-la em um laço de retry sem tocar no código da requisição. Essa separação compensa assim que você passa de uma única imagem para processamento em lote.

Enviando entrada de texto e imagem

Texto para imagem é o caso simples. A parte interessante do nano-banana é combinar prompt com imagens de referência em uma única requisição. Você passa image_input como um array de URLs e o prompt descreve como usá-las.

result = create_image(
    prompt="the person in the first image standing in a Tokyo alley at night, neon reflections, cinematic",
    image_input=["https://example.com/portrait.jpg"],
)

Dicas práticas para modalidade mista:

  • Defina papéis com clareza. Se você enviar várias imagens, indique no prompt qual fornece o sujeito, qual fornece o estilo e qual fornece o fundo. "Combine estas imagens" é vago; "use o rosto da imagem um e a iluminação da imagem dois" não é.
  • Use URLs acessíveis e estáveis. A API precisa conseguir buscar as imagens. URLs assinadas que expiram em poucos minutos funcionam para chamadas em tempo real, mas falham se você tentar novamente uma tarefa enfileirada mais tarde.
  • Prefira referências limpas. Retratos nítidos e bem iluminados produzem semelhança mais fiel do que fotos de grupo cheias de elementos. Lixo entra, lixo sai é ainda mais evidente em condicionamento por imagem do que em texto.
  • Itere primeiro no prompt, não na imagem de referência. Se a semelhança estiver errada, ajuste primeiro a forma como descreve o sujeito e só depois troque a imagem de origem — geralmente é a mudança mais simples.

Quando a imagem de referência contém um personagem que você pretende reutilizar em várias gerações, você entra no território da consistência de personagem, assunto da próxima seção.

Opções de consistência de personagem

Consistência de personagem responde a uma necessidade específica: gerar várias imagens que pareçam apresentar o mesmo personagem ou sujeito. O nano-banana expõe isso como um controle opcional, e não como um modelo separado.

Em alto nível, os trade-offs são mais ou menos assim:

  • Sem controle de consistência. Melhor para imagens avulsas. Rápido e simples, mas o personagem pode variar se você gerar novamente. Use para fundos, objetos e ilustrações independentes.
  • Consistência baseada em referência. Você fornece uma ou mais imagens do personagem e ativa a opção de consistência para que o modelo se ancore nelas. É a escolha certa para storyboards, quadrinhos e sequências de produto, em que a identidade precisa se manter entre quadros. O custo é que você precisa ter primeiro uma boa imagem de referência.
  • Consistência por descrição textual. Você descreve o personagem em detalhes em cada prompt — cor do cabelo, roupa, porte físico, características marcantes — sem usar referência. Útil quando você não tem uma foto de origem, mas exige disciplina: coloque a descrição em uma constante reutilizável para que não varie entre chamadas.

Um fluxo de trabalho pragmático para uma série:

  1. Gere o primeiro quadro sem consistência e itere no prompt até o personagem parecer correto.
  2. Use essa saída como sua imagem de referência.
  3. Ative o controle de consistência e gere os quadros seguintes, mantendo a referência fixa.
  4. Se o personagem ainda variar, encurte o prompt e deixe a referência fazer mais trabalho — prompts longos que contradizem a referência confundem o modelo.

Consistência é um ajuste, não uma garantia. Mesmo em um pipeline bem calibrado, espere revisar as saídas e regerar uma pequena fração dos quadros.

Consultando tarefas assíncronas

Quando você envia com async: true, recebe de volta um id de tarefa. A consulta é direta, mas os detalhes importam mais do que parecem.

Um poller mínimo e correto:

def wait_for_job(job_id, timeout=300, initial_delay=2, max_delay=15):
    deadline = time.time() + timeout
    delay = initial_delay
    while time.time() < deadline:
        resp = requests.get(
            f"{BASE}/v1/images/generations/{job_id}",
            headers=HEADERS,
            timeout=30,
        )
        resp.raise_for_status()
        data = resp.json()

        status = data.get("status")
        if status == "succeeded":
            return data
        if status in ("failed", "cancelled"):
            raise RuntimeError(f"job {job_id} ended as {status}: {data}")

        time.sleep(delay)
        delay = min(delay * 1.5, max_delay)

    raise TimeoutError(f"job {job_id} did not finish within {timeout}s")

Justificativa de cada escolha:

  • Backoff aproximadamente exponencial com teto. Comece com alguns segundos, multiplique por 1,5 até o limite. Consultar a cada segundo fixo em uma tarefa de trinta segundos desperdiça dezenas de requisições; consultar a cada trinta segundos fixos adiciona latência desnecessária a tarefas rápidas.
  • Prazo final rígido. Sem ele, uma tarefa travada bloqueia seu worker indefinidamente. Prefira falhar de forma explícita.
  • Tratamento de estados finais. Trate failed e cancelled como exceções, não como estados para continuar consultando. Uma tarefa em estado de falha nunca terá sucesso na próxima consulta.
  • Faça um novo GET a cada iteração. Não armazene em cache a primeira resposta e presuma que ela será atualizada.

Para trabalho em lote, envie todas as tarefas primeiro, colete os ids e depois consulte-as em laço — assim os tempos de geração se sobrepõem entre tarefas em vez de serem serializados.

Baixando e validando a saída

Após o sucesso da tarefa, a carga do resultado contém uma URL de saída. O download é um GET comum, mas você não deve presumir que esses bytes sejam uma imagem antes de verificar.

def download_image(url, path):
    with requests.get(url, stream=True, timeout=60) as r:
        r.raise_for_status()
        content_type = r.headers.get("Content-Type", "")
        if not content_type.startswith("image/"):
            raise RuntimeError(f"unexpected content type: {content_type}")
        with open(path, "wb") as f:
            for chunk in r.iter_content(chunk_size=8192):
                f.write(chunk)
    return path

Verificações de sanidade que valem a pena em todo download:

  • Content-Type. Se você pediu uma imagem e recebeu application/json ou text/html, na verdade recebeu uma página de erro. Verificar o cabeçalho detecta isso antes de você gravar um arquivo corrompido.
  • Streaming. Gravar em disco em streaming em vez de carregar o corpo inteiro da resposta na memória; isso mantém a memória estável quando você baixa em paralelo.
  • Tamanho diferente de zero. Um arquivo de zero bytes significa que algo deu errado mesmo com status HTTP normal.
  • Registre o checksum ou a contagem de bytes nos logs. Registre junto com o id da tarefa para poder rastrear um arquivo ruim até a geração específica.

A URL de saída pode ser temporária. Se você pretende manter os resultados, baixe para o seu próprio armazenamento na mesma execução, em vez de guardar o link para usar depois.

Pontos de falha comuns

A maioria dos problemas se encaixa em algumas categorias. Verifique estes pontos antes de presumir que o modelo está se comportando de forma estranha.

Autenticação e roteamento

  • Um cabeçalho Authorization ausente ou malformado retorna erro de autenticação, não uma imagem. Confirme que o cabeçalho está como Bearer <key>, com um espaço no meio.
  • Enviar um valor errado de model roteia para o lugar errado ou falha diretamente. Ele deve ser exatamente nano-banana.

Problemas no corpo da requisição

  • Prompts vagos. A causa única mais comum de "saída ruim". Antes de mudar qualquer outra coisa, adicione detalhes de composição, iluminação e estilo.
  • URLs de imagem inacessíveis. URLs de referência que retornam 404, exigem autenticação ou apontam para um caminho local falham ou são silenciosamente ignoradas. Busque a URL você mesmo uma vez para confirmar que funciona.
  • Enviar async: false com um prompt longo e atingir o timeout do cliente. Mude para assíncrono e consulte, em vez de aumentar o timeout.

Processamento de tarefas

  • Consultar uma tarefa que nunca existiu. Um erro de digitação no id da tarefa gera um erro de não encontrado que parece uma falha; registre os ids que você enviou.
  • Tratar failed como se ainda estivesse em execução. Sempre saia do laço de consulta nos estados finais.
  • Sem prazo final. O bug mais caro em tarefas em lote: uma única imagem travada arrasta a execução inteira.

Processamento da saída

  • Não verificar o Content-Type. Como dito acima, uma página de erro salva com extensão .png depois parece apenas uma imagem corrompida.
  • Depender de uma URL de resultado vencida horas após a geração. Baixe em tempo hábil.

Seguindo a ordem autenticação, corpo da requisição, ciclo de vida da tarefa e download, a maioria das falhas se resolve em poucos minutos.

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