Tutorial
Cómo usar nano-banana-pro a través de la API de HiAPI: ejemplos con curl y Python
Qué te ofrece nano-banana-pro en HiAPI
nano-banana-pro es un modelo de generación de imágenes al que puedes llamar a través de la API unificada de HiAPI. En lugar de integrarte directamente con el proveedor del modelo, solo tienes que enviar a HiAPI una solicitud con el formato unificado para obtener la imagen generada: la misma autenticación, las mismas convenciones de endpoint y el mismo envoltorio de respuesta que con los modelos de otras plataformas.
nano-banana-pro está diseñado para la generación de imágenes guiada por prompts y permite adjuntar imágenes de referencia además del prompt de texto. Las imágenes de referencia te permiten guiar la salida con contexto visual (sujeto, estilo, composición) en lugar de describirlo todo con palabras. La salida se entrega en resolución 2K, por lo que el modelo es más adecuado para activos de producción que para bocetos rápidos.
Al terminar este tutorial tendrás una integración completa y funcional que puede:
- enviar un prompt a nano-banana-pro a través de HiAPI con una solicitud
curlverificada; - reproducir la misma llamada en Python sin cambiar el payload de la solicitud;
- adjuntar una imagen de referencia a la solicitud;
- enviar tareas asíncronas, consultar su estado hasta que terminen y gestionar los reintentos de forma razonable;
- descargar la imagen 2K terminada y almacenarla en tu propia infraestructura.
Cada sección se apoya en la anterior, así que el camino más seguro es avanzar en orden: primero confirma la solicitud a mano y después llévala al código.
Antes de enviar la primera solicitud
Necesitas tres cosas antes de la primera llamada.
Una clave de API
Crea una clave de API en tu cuenta de HiAPI y asegúrate de que no quede incluida en el control de versiones. Trátala como cualquier otro secreto: cárgala desde una variable de entorno (HIAPI_API_KEY) o desde un gestor de secretos y nunca la codifiques directamente en archivos que puedas llegar a confirmar. Si la clave se filtra, rótala en el panel de control en lugar de intentar borrarla del historial.
Un cliente HTTP
Cualquier herramienta capaz de enviar solicitudes HTTPS sirve. Para la primera solicitud, curl en la línea de comandos es ideal porque no hay ningún framework entre tú y la red: cada cabecera y cada byte del cuerpo de la respuesta son visibles. Para la parte de Python, la biblioteca estándar urllib.request es suficiente; si ya usas requests o httpx, la correspondencia es la misma. Este tutorial limita Python a la biblioteca estándar, así que no necesitas instalar nada adicional.
Un lugar donde guardar la salida
Como las tareas de nano-banana-pro son asíncronas, planifica desde el principio el directorio o el bucket donde guardarás las imágenes terminadas. Para desarrollo basta una carpeta local output/; en producción, el destino debería ser el almacenamiento de objetos.
No necesitas instalar el SDK del proveedor. HiAPI funciona mediante llamadas HTTPS normales, así que la solicitud que construyes en curl es idéntica a la que enviará después tu cliente de Python.
Una solicitud curl funcional, explicada paso a paso
La forma más rápida de confirmar que la clave, el endpoint y el payload son correctos es enviar una solicitud a mano. Antes de escribirla, desglósala en sus partes.
Endpoint y método
Todas las llamadas a modelos se envían a la misma URL base de HiAPI, y el modelo se identifica en el cuerpo de la solicitud, no en la ruta. Las solicitudes de generación usan POST.
Cabeceras
Hay tres cabeceras importantes en cada llamada:
Authorization: Bearer $HIAPI_API_KEY: tu clave, enviada como token bearer.Content-Type: application/json: para solicitudes que solo contienen un prompt.Accept: application/json: para recibir una respuesta estructurada en lugar de una página de error HTML.
Cuerpo de la solicitud
El cuerpo JSON transporta el nombre del modelo y los parámetros de generación. Como mínimo, debes enviar el identificador del modelo y tu prompt:
curl -sS -X POST "$HIAPI_BASE_URL" \
-H "Authorization: Bearer $HIAPI_API_KEY" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"model": "nano-banana-pro",
"input": {
"prompt": "a glass terrarium on a sunlit windowsill, soft morning light, shallow depth of field"
}
}'
Antes de ejecutar este comando, define HIAPI_BASE_URL y HIAPI_API_KEY en tu shell. Guardar la URL en una variable significa que más adelante podrás apuntar el mismo script a una URL de staging o de sandbox sin modificar el comando.
Leer la respuesta
Un envío correcto devuelve un objeto JSON que identifica la tarea. El campo importante es el identificador de la tarea: lo usarás en el paso de consulta de estado. Como la generación tarda, no esperes obtener la imagen en la primera respuesta; nano-banana-pro funciona de forma asíncrona y esta llamada es solo la parte de envío de la interacción.
Si el comando devuelve un error, detente aquí y corrígelo antes de escribir código. La solicitud curl verificada es tu implementación de referencia: cada paso posterior debe producir la misma solicitud HTTP.
Llevar la solicitud a Python
La migración es mecánica: la URL, el método, las cabeceras y el cuerpo JSON permanecen igual. Solo cambia el cliente.
La misma solicitud con urllib
import json
import os
import urllib.request
api_key = os.environ["HIAPI_API_KEY"]
base_url = os.environ["HIAPI_BASE_URL"]
payload = {
"model": "nano-banana-pro",
"input": {
"prompt": "a glass terrarium on a sunlit windowsill, soft morning light, shallow depth of field"
},
}
request = urllib.request.Request(
base_url,
data=json.dumps(payload).encode("utf-8"),
method="POST",
headers={
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
"Accept": "application/json",
},
)
with urllib.request.urlopen(request, timeout=60) as response:
body = json.loads(response.read().decode("utf-8"))
print(body)
Reglas de correspondencia
Al convertir desde curl, hay tres detalles que suelen causar errores:
- El cuerpo debe ser bytes.
json.dumpsgenera una cadena; hay que codificarla antes de pasarla al objeto de solicitud. - Las cabeceras son un diccionario, no flags. Cada línea
-Hse convierte en un par clave/valor. Conserva el prefijoBearery el espacio que le sigue. - El método debe especificarse explícitamente. Cuando hay datos,
urllib.requestusaPOSTde forma predeterminada, pero establecermethod="POST"hace explícita la intención y evita sorpresas si más adelante eliminas el cuerpo de la solicitud.
Si usas requests, la forma equivalente es requests.post(base_url, headers=headers, json=payload, timeout=60): json= se encarga de la serialización y del tipo de contenido por ti. Si usas httpx, la forma de la llamada es la misma.
Una pequeña función auxiliar que vale la pena tener
Coloca la llamada de envío dentro de una función que reciba el prompt y devuelva el identificador de la tarea. Cada sección posterior (imágenes de referencia, consulta de estado, descarga) se apoya en esa función, y mantenerla en un solo lugar significa que los cambios en la forma de la solicitud ocurren una sola vez.
Enviar imágenes de referencia junto con el prompt
Las imágenes de referencia cambian la forma de la solicitud, así que decide cuanto antes qué tipo de entrada usará tu aplicación.
Dos opciones prácticas
Entrada basada en URL. Si la imagen de referencia ya es accesible por HTTPS, puedes adjuntar su URL junto con el prompt en el cuerpo de la solicitud. Esto mantiene la solicitud como application/json, la forma más fácil de reintentar y registrar.
Subir un archivo. Si la imagen está en el cliente o en almacenamiento privado, puedes enviarla como parte de archivo en una solicitud multipart/form-data. En ese caso, la cabecera Content-Type: application/json se sustituye por el tipo de contenido multipart (deja que tu cliente HTTP lo genere para asegurar que el boundary sea correcto) y los campos JSON pasan a ser campos de formulario dentro de la misma solicitud.
Para la mayoría de las integraciones del lado del servidor, la entrada basada en URL es más sencilla: tus imágenes ya están en el almacenamiento de objetos, la solicitud sigue siendo JSON y evitas enviar dos veces cuerpos de solicitud grandes al reintentar.
Mantener sincronizados el prompt y la imagen de referencia
El prompt debe describir para qué sirve la imagen de referencia, no solo qué contiene. «Iguala la iluminación y los tonos de la imagen de referencia; cambia el sujeto por un jarrón de cerámica» le da al modelo una tarea clara. Un prompt que ignora la imagen de referencia desperdicia la entrada.
Validar antes de enviar
Las imágenes de referencia son la causa más común de errores en las solicitudes. Comprueba que la URL sea accesible públicamente (o que ambos extremos de tu sistema hayan acordado la caducidad de la URL firmada), que el tipo de archivo sea aceptado por el modelo y que la imagen no sea tan grande como para provocar un tiempo de espera agotado en la subida. Haz estas comprobaciones primero en tu propio código: depurar una validación del cliente fallida cuesta mucho menos que una solicitud rechazada.
Gestionar tareas asíncronas y consultar los resultados
La generación con nano-banana-pro es asíncrona. Estructura tu cliente en torno a tres pasos independientes: enviar, consultar el estado y descargar. Mezclarlos en una sola función dificulta razonar sobre reintentos y tiempos de espera.
Paso 1: enviar
Envía la solicitud anterior y guarda el identificador de tarea que devuelve. Persiste ese identificador en algún lugar duradero (una fila de base de datos o un mensaje de cola), porque tu proceso puede reiniciarse entre el envío y la finalización.
Paso 2: consultar el estado
Usa la misma URL base y la misma cabecera Authorization, incluyendo el identificador de la tarea en la solicitud, para obtener el estado de la tarea. Consulta a intervalos en lugar de hacer un bucle ajustado; unos segundos entre comprobaciones es lo habitual, y si varias comprobaciones consecutivas devuelven un estado pendiente, conviene aplicar un retroceso exponencial.
import time
TERMINAL = {"succeeded", "failed"}
def wait_for_job(job_id, timeout=300, interval=3):
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
status = get_job_status(job_id) # same headers as submit
if status in TERMINAL:
return status
time.sleep(interval)
raise TimeoutError(f"job {job_id} did not finish in time")
Paso 3: respetar los estados
Trata el campo de estado como una enumeración. Una tarea está en ejecución, ha terminado correctamente o ha fallado. Gestiona explícitamente los tres casos: si tiene éxito, pasa a la descarga; si falla, lee el mensaje de error y determina si el problema está en la entrada o en el prompt; si se agota el tiempo, deja de consultar y expón el identificador de la tarea para investigarlo más adelante.
Los reintentos pertenecen a dos niveles
Los reintentos a nivel de red deben aplicarse a la llamada de envío y a cada solicitud de consulta de estado, con un número limitado de intentos. Los reintentos a nivel de tarea son distintos: si una tarea falla por un prompt o una imagen de referencia deficientes, volver a enviar el mismo payload fallará de la misma manera. Reintenta la tarea en sí solo después de cambiar la entrada.
Un patrón de producción habitual es que un worker se encargue del bucle de consulta de estado, de modo que las solicitudes web nunca se bloqueen esperando la generación. Envía desde tu manejador de API, entrega el identificador de la tarea al worker y notifica al cliente cuando el worker termine.
Descargar y almacenar la salida en 2K
La respuesta final de la tarea apunta a la imagen generada en lugar de incluirla, lo que significa que la descarga es un paso del que tú eres responsable.
Obtener la imagen
Cuando la tarea informa de éxito, la respuesta contiene la ubicación de la salida. Recupérala con una solicitud GET normal y luego escribe los bytes en un archivo o transmítelos en streaming al almacenamiento. Durante el desarrollo, guarda en un directorio local con un nombre predecible construido a partir del identificador de la tarea, para poder asociar el archivo con el registro de la tarea.
import pathlib
import urllib.request
def save_image(url, job_id, out_dir="output"):
pathlib.Path(out_dir).mkdir(exist_ok=True)
target = pathlib.Path(out_dir) / f"nano-banana-pro-{job_id}.png"
with urllib.request.urlopen(url, timeout=60) as response:
target.write_bytes(response.read())
return target
Almacenar correctamente
En producción, transmite la respuesta directamente en streaming al almacenamiento de objetos en lugar de escribirla en el disco local, de modo que el comportamiento sea el mismo independientemente del worker que procese la tarea. Registra la clave del objeto y el identificador de la tarea en la base de datos, y comprueba que el tipo de contenido y la extensión del archivo coincidan con lo que genera el modelo. Si la imagen se va a servir en una página web, genera las miniaturas en el mismo proceso para que las páginas públicas nunca carguen el archivo completo en resolución 2K.
Planificar la política de retención
La salida generada son tus datos. Decide cuánto tiempo conservarla, si necesitas control de versiones y si el destino final es algo que tu aplicación puede regenerar si se pierde. Si la respuesta es negativa, guarda también el payload de la solicitud junto con la imagen, para que un futuro bug o un cambio de modelo nunca te impida reproducir el resultado.
Puntos de fallo habituales y cómo depurarlos
La mayoría de las primeras integraciones fallan por unas pocas causas. Antes de suponer que el modelo tiene un problema, revisa esta lista punto por punto.
- Errores de autorización. Falta el prefijo
Bearer, la clave tiene un salto de línea final por haberse copiado de un archivo de entorno, o la clave que usas pertenece a un entorno distinto del de la URL base configurada. - Tipos de contenido que no coinciden. Enviar campos JSON con una cabecera multipart, o enviar un archivo con
application/json, es la causa más común de que se rechacen las solicitudes con imagen de referencia. Para los cuerpos multipart, deja que el cliente configure la cabecera. - Método o forma del cuerpo incorrectos. Usar GET en lugar de POST, o usar un
promptde nivel superior en lugar del objeto anidadoinput, produce errores de validación que parecen totalmente ajenos. Compara tu solicitud byte a byte con la llamadacurlverificada;curl -vmuestra las cabeceras que realmente se envían. - Consultar demasiado rápido o no consultar nunca. Solicitar el estado con demasiada frecuencia provoca limitación de tasa; no consultar nunca no devuelve nada. Usa un intervalo fijo con retroceso y un tiempo de espera máximo.
- URL de salida caducada. Si esperas demasiado entre la finalización de la tarea y la descarga, la URL de salida firmada puede caducar. Descarga inmediatamente después del éxito o prepara una ruta de recuperación que obtenga una nueva ubicación.
- Fallos silenciosos en el código. Registra siempre el código de estado HTTP y el cuerpo de la respuesta cuando haya un error. El mensaje genérico de excepción que lanza el cliente HTTP oculta la parte útil de la respuesta.
Cuando algo no esté claro, vuelve a la solicitud curl verificada de la primera sección. Si esa solicitud funciona y tu llamada en Python no, la diferencia entre ambas es el bug.
Artículos relacionados
Ver todos los artículos →-
Tutorial
Cómo llamar a hailuo-2.3-fast/image-to-video a través de la API de HiAPI: práctica con curl y Python
La primera solicitud ejecutable del nivel rápido y de bajo coste de imagen a vídeo: del ID del modelo a la descarga del MP4
-
Tutorial
Cómo llamar a hailuo-2.3/text-to-video mediante la API de HiAPI: curl, Python y ejemplos de solicitud ejecutables
-
Tutorial
Uso de Kandinsky 6.0 Pro texto a video en HiAPI: tutorial con curl y Python
Demostración práctica centrada en la solicitud: construir la llamada de prompt a clip de video, enviar la solicitud, sondear el estado de finalización y descargar el resultado