教程
如何通过 HiAPI API 使用 nano-banana-pro:curl 与 Python 示例
HiAPI 上的 nano-banana-pro 能为你提供什么
nano-banana-pro 是一款可通过 HiAPI 统一 API 调用的图像生成模型。你无需直接与模型提供商集成,只需向 HiAPI 发送统一格式的请求,即可获得生成的图像——与其他平台模型一样,使用相同的身份验证、相同的端点约定和相同的响应封装。
nano-banana-pro 专为提示词驱动的图像生成而设计,并可在文本提示词之外附带参考图像。参考图像让你能够用视觉上下文(主体、风格、构图)来引导输出,而不必用文字描述一切。输出以 2K 分辨率交付,因此该模型更适合生产资产,而非快速草稿。
完成本教程后,你将得到一个完整可用的集成,它可以:
- 通过经过验证的
curl请求向 HiAPI 发送提示词至 nano-banana-pro; - 在 Python 中复现相同的调用,无需更改请求负载;
- 在请求中附带参考图像;
- 提交异步任务、轮询直至完成,并合理处理重试;
- 下载完成的 2K 图像并存储到你自己的基础设施中。
每一节都建立在前一节的基础上,因此最稳妥的路径是按顺序操作:先手动确认请求,再将其移入代码。
发送第一个请求之前
首次调用前你需要准备三样东西。
一个 API 密钥
在你的 HiAPI 账户中创建一个 API 密钥,并确保它不被纳入源码管理。像对待任何其他机密一样对待它:从环境变量(HIAPI_API_KEY)或密钥管理器中加载,绝不要硬编码在你可能提交的文件中。如果密钥泄露,请在仪表板中轮换它,而不是试图从历史记录中清除。
一个 HTTP 客户端
任何能发送 HTTPS 请求的工具都可以。对于第一个请求,命令行中的 curl 是理想选择,因为你和网络之间没有框架——每个请求头和响应体的每个字节都可见。对于 Python 部分,标准库的 urllib.request 就足够了;如果你已经在使用 requests 或 httpx,映射关系是一样的。本教程将 Python 限制在标准库内,因此无需额外安装任何东西。
一个存放输出的位置
由于 nano-banana-pro 任务是异步的,从一开始就要规划好用于保存完成图像的目录或存储桶。开发时本地 output/ 文件夹即可;生产环境则应以对象存储为目标。
你无需安装供应商 SDK。HiAPI 通过普通 HTTPS 调用,因此你在 curl 中构建的请求,与你之后 Python 客户端发送的请求完全相同。
一个可用的 curl 请求,分步说明
确认密钥、端点和负载都正确的最快方式是手动发送一个请求。在输入之前先将其拆解为各个部分。
端点与方法
所有模型调用都发往同一个 HiAPI 基础 URL,模型在请求体中标识,而非在路径中标识。生成请求使用 POST。
请求头
每次调用有三个重要的请求头:
Authorization: Bearer $HIAPI_API_KEY——你的密钥,作为 bearer 令牌传递。Content-Type: application/json——用于仅含提示词的请求。Accept: application/json——以便获得结构化响应,而非 HTML 错误页面。
请求体
JSON 请求体携带模型名称和生成参数。至少需要发送模型标识符和你的提示词:
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"
}
}'
运行此命令前,请在 shell 中设置 HIAPI_BASE_URL 和 HIAPI_API_KEY。将 URL 保存在变量中意味着以后你可以将同一脚本指向暂存或沙箱 URL,而无需修改命令。
读取响应
成功提交会返回一个标识任务的 JSON 对象。重要的字段是任务标识符——你将在轮询步骤中使用它。由于生成需要时间,不要期望在首次响应中就能拿到图像本身;nano-banana-pro 以异步方式运行,此调用只是交互中的提交部分。
如果命令返回错误,请在此处停下,在编写任何代码之前修复它。经过验证的 curl 请求就是你的参考实现:后续每一步都应产生相同的 HTTP 请求。
将请求移植到 Python
移植是机械性的:URL、方法、请求头和 JSON 请求体保持不变。只有客户端发生变化。
使用 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)
映射规则
从 curl 转换时,有三个细节容易出错:
- 请求体必须是字节。
json.dumps生成字符串;在交给请求对象之前需要编码。 - 请求头是字典,不是标志。 每个
-H行变成一个键/值对。保留Bearer前缀及其后的空格。 - 方法必须显式指定。 当存在数据时,
urllib.request默认使用POST,但设置method="POST"能让意图更明确,并在你以后移除请求体时防止意外。
如果你使用 requests,等效写法是 requests.post(base_url, headers=headers, json=payload, timeout=60)——json= 会为你处理序列化和内容类型。如果你使用 httpx,调用形式相同。
一个值得拥有的小辅助函数
将提交调用放入一个接收提示词并返回任务标识符的函数中。后续每一节——参考图像、轮询、下载——都建立在该函数之上,将其保存在一个地方意味着请求形式的变化只发生一次。
随提示词发送参考图像
参考图像会改变请求的形式,因此要尽早决定你的应用程序将使用哪种输入方式。
两种实用选项
基于 URL 的输入。 如果参考图像已可通过 HTTPS 访问,你可以在请求体中附带其 URL 以及提示词。这使请求保持为 application/json,这是最容易重试和记录的形式。
上传文件。 如果图像位于客户端或私有存储中,你可以将其作为文件部分在 multipart/form-data 请求中发送。在这种情况下,Content-Type: application/json 请求头会被 multipart 内容类型替换——让你的 HTTP 客户端生成它以确保边界正确——JSON 字段则成为同一请求中的表单字段。
对于大多数服务器端集成,基于 URL 的输入更简单:你的图像已经在对象存储中,请求保持为 JSON,并且你在重试时避免了两次发送大请求体。
保持提示词与参考图像同步
提示词应描述参考图像的用途,而不仅仅是其内容。“匹配参考图像的光照和色调;将主体改为陶瓷花瓶”给了模型一个明确的任务。忽略参考图像的提示词会浪费输入。
发送前验证
参考图像是请求错误最常见的来源。检查 URL 是否可公开访问(或者你系统两端是否就签名 URL 过期时间达成一致)、文件类型是否为模型接受、图像是否大到导致上传超时。先在你自己的代码中完成这些检查——客户端验证失败的调试成本远低于被拒绝的请求。
处理异步任务并轮询结果
nano-banana-pro 生成是异步的。围绕三个独立步骤构建你的客户端:提交、轮询、下载。将它们混入一个函数会使重试和超时难以推理。
步骤 1:提交
发送上述请求并存储其返回的任务标识符。将该标识符持久化到某个持久位置——数据库行或队列消息——因为你的进程可能在提交和完成之间重启。
步骤 2:轮询
使用相同的基础 URL 和相同的 Authorization 请求头,在请求中携带任务标识符来获取任务状态。按间隔轮询而非 tight loop;检查之间间隔几秒是典型的,如果连续多次检查返回待处理状态,则应当退避。
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")
步骤 3:尊重状态
将状态字段视为枚举。任务要么仍在运行,要么成功完成,要么失败。显式处理所有三种情况:成功时进入下载;失败时读取错误信息并判断是输入还是提示词的问题;超时时停止轮询并暴露任务标识符以便后续调查。
重试属于两个层级
网络级重试应应用于提交调用和每个轮询请求,并设置有限的尝试次数。任务级重试则不同:如果任务因糟糕的提示词或参考图像而失败,重新提交相同的负载将以同样的方式失败。只有在更改输入后才重试任务本身。
一种常见的生产模式是由一个 worker 负责轮询循环,这样 Web 请求永远不会因生成而阻塞。从你的 API 处理程序提交,将任务标识符交给 worker,并在 worker 完成时通知客户端。
下载并存储 2K 输出
任务的最终响应指向生成的图像而非内嵌它,这意味着下载是你需要负责的步骤。
获取图像
当任务报告成功时,响应包含输出的位置。用普通的 GET 请求检索它,然后将字节写入文件或流式传输到存储。开发时,使用由任务标识符构建的可预测名称保存到本地目录,以便将文件与任务记录关联。
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
正确存储
在生产环境中,将响应直接流式传输到对象存储而非写入本地磁盘,这样无论哪个 worker 处理任务,行为都保持一致。在数据库中记录对象键和任务标识符,并检查内容类型和文件扩展名是否与模型生成的一致。如果图像将在网页上提供,请在同一管道中生成缩略图,以便公共页面永远不加载完整的 2K 分辨率文件。
规划保留策略
生成的输出是你的数据。决定保留多长时间、是否需要版本控制,以及最终位置是否是你的应用在丢失后能重新生成的。如果答案是否定的,请同时存储请求负载和图像,这样未来的 bug 或模型变更永远不会让你无法复现结果。
常见故障点及调试方法
大多数首次集成因少数几个原因失败。在假设模型有问题之前,请逐项检查此清单。
- 授权错误。 缺少
Bearer前缀、密钥因从环境文件复制而带有尾随换行符,或者你使用的密钥来自与所设基础 URL 不同的环境。 - 内容类型不匹配。 使用 multipart 请求头发送 JSON 字段,或使用
application/json发送文件,是参考图像请求被拒绝的最常见原因。对于 multipart 请求体,让客户端设置请求头。 - 错误的方法或请求体形式。 使用 GET 而非 POST,或使用顶层
prompt而非嵌套的input对象,会产生看起来完全不相关的验证错误。将你的请求与经过验证的curl调用逐字节比较——curl -v显示实际发送的请求头。 - 轮询过快或根本不轮询。 频繁请求状态端点会导致速率限制;从不轮询则什么也得不到。使用带退避的固定间隔和硬超时。
- 输出 URL 过期。 如果在任务完成和下载之间等待太久,签名的输出 URL 可能会过期。成功之后立即下载,或准备一条获取新位置的重新获取路径。
- 代码中的静默失败。 出错时始终记录 HTTP 状态码和响应体。HTTP 客户端抛出的一般异常消息会隐藏响应中有用的部分。
当有不清楚的地方时,回退到第一节中经过验证的 curl 请求。如果该请求有效而你的 Python 调用无效,两者之间的差异就是 bug。