PaperBanana API 文档
通过异步任务式 API 生成出版级方法论图、优化插图提示词、生成统计图并拼接组合图,默认文档语言可一键切换。
简体中文
概述
PaperBanana 是一个用于生成出版级学术插图的 AI API, 当前支持五类工作流:
- 方法论图表,适用于架构图、流程图、研究工作流图
- PDF 生成方法论图,用于从论文 PDF 指定页码抽取文本后生成图
- 插图提示词优化,用于在正式生成前润色用户提供的图表描述
- 统计图表,适用于柱状图、折线图、直方图、散点图等
- 组合图拼接,用于把多个已完成任务的输出拼成一张带子图标签的多 panel 图
API 使用异步任务工作流:先提交请求拿到 task_id,
再轮询任务状态直到结果完成。大多数任务会在 30 到 120 秒内完成,具体取决于复杂度和迭代次数。
基础 URL
https://api.paperbanana.me
本文档中的所有接口路径都基于这个基础 URL。
身份认证
所有生成接口和管理接口都需要在 Authorization 请求头中携带 Bearer 令牌:
Authorization: Bearer pb_xxxxxxxxxxxxxxxxxxxxxxxxxxxx
| 令牌类型 | 访问范围 | 获取方式 |
|---|---|---|
API Key |
/api/generate、/api/generate-from-pdf、/api/optimize-prompt、/api/plot、/api/composite、/api/tasks/* |
由管理员通过 POST /api/admin/keys 或管理面板创建 |
Admin Key |
/api/admin/* |
通过环境变量 PAPERBANANA_ADMIN_KEY 配置 |
generate、generate-from-pdf、optimize-prompt、plot 或 composite 任务时会立即扣除 1 个配额单位。
典型工作流程
标准接入通常包含三步:
1. POST /api/generate (或 /api/generate-from-pdf、/api/optimize-prompt、/api/plot 或 /api/composite) -> { task_id, status: "pending" }
2. GET /api/tasks/{task_id} -> { status: "running" }
3. GET /api/tasks/{task_id} -> { status: "completed", description: "..." }
可选:
GET /api/tasks/{task_id}/image -> 对产出图片的任务直接下载 PNG
GET /api/tasks/{task_id}/image?format=svg -> 若统计图任务产出了 SVG,则可直接下载
错误处理
| 状态码 | 含义 | 常见原因 |
|---|---|---|
401 | 未授权 | 缺少或无效的 API Key / Admin Key |
403 | 禁止访问 | API Key 被禁用或配额已耗尽 |
404 | 未找到 | 任务 ID 不存在、无权限访问,或图片尚不可用 |
422 | 参数校验失败 | 缺少必填字段,或 data_json 不是合法 JSON 字符串 |
500 | 服务器错误 | 内部配置或运行时异常 |
错误响应统一返回 JSON:
{
"detail": "可读的错误信息"
}
健康检查
GET/api/health
用于确认 API 服务是否在线。这个接口不需要认证。
响应 200
{
"status": "ok",
"version": "0.1.2"
}
curl https://api.paperbanana.me/api/health
const res = await fetch("https://api.paperbanana.me/api/health");
const data = await res.json();
console.log(data);
import requests
r = requests.get("https://api.paperbanana.me/api/health")
print(r.json())
生成方法论图表
POST/api/generate
提交方法论文本和图标题,生成学术风格的架构图、工作流图或系统总览图。 接口会立即返回一个任务 ID。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
source_context | string | 是 | 方法论文本或论文相关摘录 |
caption | string | 是 | 图表标题,例如 Figure 1: Transformer encoder architecture |
iterations | integer | 否 | 优化迭代次数,范围 1 到 10,默认值为 3 |
aspect_ratio | string | 否 | 可选的目标长宽比。支持 auto、1:1、5:4、2:3、3:2、3:4、4:5、4:3、9:16、16:9、21:9。不传时保留服务端当前默认布局 |
output_formats | array[string] | 否 | 可选的方法论图导出格式。仅支持 png。轮询结果里的 PNG 预览会继续保留 |
vlm_provider | string | 否 | 可选的 VLM 供应商覆盖值。支持 gemini、openrouter、kie、apimart。不传时沿用服务端当前默认 provider |
vlm_model | string | 否 | 可选的 VLM 模型覆盖值。不传时保持当前模型,或在切换 provider 时回落到对应默认模型 |
image_model | string | 否 | 可选的 Kie 图片模型覆盖值。支持 nano-banana-pro、google/nano-banana、nano-banana-2 和 gpt-image-2-text-to-image,仅当服务端图片 provider 为 kie_imagen 时生效 |
image_resolution | string | 否 | 可选的 Kie 图片分辨率覆盖值。支持 1K、2K、4K。nano-banana-pro、nano-banana-2 和 gpt-image-2-text-to-image 支持;google/nano-banana 不支持该参数。GPT Image 2 中 aspect_ratio: "auto" 仅支持 1K,1:1 不支持 4K |
webhook_url | string | 否 | 可选回调地址,用于接收最终结果 |
webhook_include_image | boolean | 否 | 若为 true,webhook 载荷中会包含 image_base64 |
响应 202 Accepted
{
"task_id": "a1b2c3d4e5f6...",
"status": "pending"
}
{
"vlm_provider": "kie",
"vlm_model": "gemini-2.5-flash"
}
如需切到 APIMart:
{
"vlm_provider": "apimart",
"vlm_model": "gemini-2.5-flash"
}
{
"image_model": "gpt-image-2-text-to-image",
"aspect_ratio": "16:9",
"image_resolution": "4K"
}
PaperBanana 会把 GPT Image 2 原生的 aspect_ratio 与 resolution 字段传给 Kie。若 aspect_ratio 为 auto,请使用 1K。
curl -X POST https://api.paperbanana.me/api/generate \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"source_context": "Our model uses a 6-layer Transformer encoder with multi-head self-attention.",
"caption": "Figure 1: Transformer encoder architecture",
"iterations": 3,
"aspect_ratio": "16:9",
"output_formats": ["png"],
"vlm_provider": "kie",
"vlm_model": "gemini-2.5-flash",
"image_model": "nano-banana-pro",
"image_resolution": "4K"
}'
const API_KEY = "pb_your_api_key_here";
const BASE = "https://api.paperbanana.me";
const submitRes = await fetch(`${BASE}/api/generate`, {
method: "POST",
headers: {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
source_context: "Our model uses a 6-layer Transformer encoder...",
caption: "Figure 1: Transformer encoder architecture",
iterations: 3,
aspect_ratio: "16:9",
output_formats: ["png"],
vlm_provider: "kie",
vlm_model: "gemini-2.5-flash",
image_model: "nano-banana-pro",
image_resolution: "4K"
})
});
const { task_id } = await submitRes.json();
while (true) {
const res = await fetch(`${BASE}/api/tasks/${task_id}`, {
headers: { "Authorization": `Bearer ${API_KEY}` }
});
const task = await res.json();
if (task.status === "completed" || task.status === "failed") {
console.log(task);
break;
}
await new Promise(resolve => setTimeout(resolve, 5000));
}
import time
import requests
API_KEY = "pb_your_api_key_here"
BASE = "https://api.paperbanana.me"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
r = requests.post(f"{BASE}/api/generate", headers=HEADERS, json={
"source_context": "Our model uses a 6-layer Transformer encoder...",
"caption": "Figure 1: Transformer encoder architecture",
"iterations": 3,
"aspect_ratio": "16:9",
"output_formats": ["png"],
"vlm_provider": "kie",
"vlm_model": "gemini-2.5-flash",
"image_model": "nano-banana-pro",
"image_resolution": "4K",
})
r.raise_for_status()
task_id = r.json()["task_id"]
while True:
task = requests.get(f"{BASE}/api/tasks/{task_id}", headers=HEADERS).json()
if task["status"] in {"completed", "failed"}:
print(task)
break
time.sleep(5)
PDF 生成方法论图
POST/api/generate-from-pdf
提交 base64 编码后的 PDF 和图标题。服务端会从全部页面或指定页码抽取文本,
然后走与 /api/generate 相同的方法论图生成流程。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
pdf_base64 | string | 是 | PDF 文件的 base64 内容,也支持 data:application/pdf;base64,... 形式的 data URL |
caption | string | 是 | 生成方法论图所需的图标题或表达意图 |
pages | string | 否 | 可选的 1-based 页码选择,例如 1、1,3-5 或 5-3。不传则使用全部页面 |
iterations | integer | 否 | 优化迭代次数,范围 1 到 10,默认值为 3 |
aspect_ratio | string | 否 | 可选的目标长宽比,可用值与 /api/generate 相同 |
output_formats | array[string] | 否 | 可选的方法论图导出格式。当前支持 png |
vlm_provider | string | 否 | 可选的 VLM provider 覆盖值。支持 gemini、openrouter、kie、apimart |
vlm_model | string | 否 | 可选的 VLM 模型覆盖值 |
image_model | string | 否 | 可选的 Kie 图片模型覆盖值 |
image_resolution | string | 否 | 可选的 Kie 图片分辨率覆盖值 |
webhook_url | string | 否 | 可选回调地址,用于接收最终结果 |
webhook_include_image | boolean | 否 | 若为 true,webhook 载荷中会包含 image_base64 |
响应 202 Accepted
{
"task_id": "a1b2c3d4e5f6...",
"status": "pending"
}
PDF_B64=$(base64 -i paper.pdf | tr -d '\n')
curl -X POST https://api.paperbanana.me/api/generate-from-pdf \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d "{
\"pdf_base64\": \"${PDF_B64}\",
\"caption\": \"Figure 1: Overall methodology pipeline\",
\"pages\": \"2-4\",
\"iterations\": 3,
\"aspect_ratio\": \"16:9\",
\"output_formats\": [\"png\"]
}"
const file = document.querySelector("input[type=file]").files[0];
const pdf_base64 = await new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(String(reader.result).split(",", 2)[1]);
reader.onerror = reject;
reader.readAsDataURL(file);
});
const res = await fetch("https://api.paperbanana.me/api/generate-from-pdf", {
method: "POST",
headers: {
"Authorization": "Bearer pb_your_api_key_here",
"Content-Type": "application/json"
},
body: JSON.stringify({
pdf_base64,
caption: "Figure 1: Overall methodology pipeline",
pages: "2-4",
output_formats: ["png"]
})
});
const { task_id } = await res.json();
import base64
import requests
API_KEY = "pb_your_api_key_here"
BASE = "https://api.paperbanana.me"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
pdf_base64 = base64.b64encode(open("paper.pdf", "rb").read()).decode("ascii")
r = requests.post(f"{BASE}/api/generate-from-pdf", headers=HEADERS, json={
"pdf_base64": pdf_base64,
"caption": "Figure 1: Overall methodology pipeline",
"pages": "2-4",
"output_formats": ["png"],
})
r.raise_for_status()
print(r.json())
优化插图提示词
POST/api/optimize-prompt
提交一段已有的图表描述,让 Stylist 阶段单独对它做润色,输出更清晰、更适合学术插图生成的高质量提示词。 这是一个纯文本任务,不会直接生成图片。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
description | string | 是 | 需要优化的提示词或详细图表描述 |
source_context | string | 否 | 可选的论文摘录或方法论上下文,用于约束优化结果不要偏题 |
caption | string | 否 | 可选的图标题或表达意图 |
diagram_type | string | 否 | 优化目标类型。支持 methodology、statistical_plot,默认值为 methodology |
guidelines | string | 否 | 可选的自定义风格规范。不传时使用服务端内置的方法图或统计图风格指南 |
vlm_provider | string | 否 | 可选的 VLM provider 覆盖值。支持 gemini、openrouter、kie、apimart |
vlm_model | string | 否 | 可选的 VLM 模型覆盖值。不传时保持当前模型,或在切换 provider 时回落到对应默认模型 |
webhook_url | string | 否 | 可选回调地址,用于接收最终结果 |
webhook_include_image | boolean | 否 | 为保持 API 一致性而保留,但该任务不会生成图片,因此此字段会被忽略 |
响应 202 Accepted
{
"task_id": "b7c8d9e0f1a2...",
"status": "pending"
}
description 字段中。
这个接口不会返回 image_base64 或可下载图片产物。
curl -X POST https://api.paperbanana.me/api/optimize-prompt \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"description": "Draw a transformer encoder with token embeddings, positional encoding, 6 encoder blocks, and a classifier head.",
"source_context": "Our model uses a 6-layer Transformer encoder with multi-head self-attention.",
"caption": "Figure 1: Transformer encoder architecture",
"diagram_type": "methodology"
}'
const res = await fetch("https://api.paperbanana.me/api/optimize-prompt", {
method: "POST",
headers: {
"Authorization": "Bearer pb_your_api_key_here",
"Content-Type": "application/json"
},
body: JSON.stringify({
description: "Draw a transformer encoder with token embeddings...",
source_context: "Our model uses a 6-layer Transformer encoder...",
caption: "Figure 1: Transformer encoder architecture",
diagram_type: "methodology"
})
});
const { task_id } = await res.json();
console.log(task_id);
import requests
r = requests.post("https://api.paperbanana.me/api/optimize-prompt", headers=HEADERS, json={
"description": "Draw a transformer encoder with token embeddings...",
"source_context": "Our model uses a 6-layer Transformer encoder...",
"caption": "Figure 1: Transformer encoder architecture",
"diagram_type": "methodology",
})
print(r.json())
生成统计图表
POST/api/plot
提交结构化数据和图表意图描述,生成适用于论文的柱状图、折线图、直方图、散点图等图表。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
data_json | string | 是 | 包含绘图数据的 JSON 字符串 |
intent | string | 是 | 对图表类型、坐标轴和风格要求的描述 |
iterations | integer | 否 | 优化迭代次数,范围 1 到 10,默认值为 3 |
aspect_ratio | string | 否 | 可选的目标画布比例。支持 auto、1:1、5:4、2:3、3:2、3:4、4:5、4:3、9:16、16:9、21:9。服务端会据此引导统计图布局 |
output_formats | array[string] | 否 | 可选的统计图导出格式。支持 png、svg、pdf。轮询结果里的 PNG 预览会继续保留 |
vlm_provider | string | 否 | 可选的 VLM 供应商覆盖值。支持 gemini、openrouter、kie、apimart。不传时沿用服务端当前默认 provider |
vlm_model | string | 否 | 可选的 VLM 模型覆盖值。不传时保持当前模型,或在切换 provider 时回落到对应默认模型 |
webhook_url | string | 否 | 可选回调地址,用于接收最终结果 |
webhook_include_image | boolean | 否 | 若为 true,webhook 载荷中会包含 image_base64 |
响应 202 Accepted
{
"task_id": "f7e8d9c0b1a2...",
"status": "pending"
}
{
"vlm_provider": "kie",
"vlm_model": "gemini-2.5-flash"
}
如需切到 APIMart:
{
"vlm_provider": "apimart",
"vlm_model": "gemini-2.5-flash"
}
data_json
这个字段必须是JSON 字符串,而不是嵌套对象本身。
Python 请先使用 json.dumps(data),JavaScript 请先使用 JSON.stringify(data)。
curl -X POST https://api.paperbanana.me/api/plot \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"data_json": "{\"models\":[\"ResNet-50\",\"ViT-B/16\",\"EfficientNet-B4\"],\"accuracy\":[76.1,81.8,82.9]}",
"intent": "Bar chart comparing model accuracy with value labels",
"iterations": 3,
"aspect_ratio": "4:3",
"output_formats": ["png", "svg", "pdf"],
"vlm_provider": "apimart",
"vlm_model": "gemini-2.5-flash"
}'
const data = {
models: ["ResNet-50", "ViT-B/16", "EfficientNet-B4"],
accuracy: [76.1, 81.8, 82.9]
};
const res = await fetch("https://api.paperbanana.me/api/plot", {
method: "POST",
headers: {
"Authorization": "Bearer pb_your_api_key_here",
"Content-Type": "application/json"
},
body: JSON.stringify({
data_json: JSON.stringify(data),
intent: "Bar chart comparing model accuracy with value labels",
iterations: 3,
aspect_ratio: "4:3",
output_formats: ["png", "svg", "pdf"],
vlm_provider: "apimart",
vlm_model: "gemini-2.5-flash"
})
});
console.log(await res.json());
import json
import requests
data = {
"models": ["ResNet-50", "ViT-B/16", "EfficientNet-B4"],
"accuracy": [76.1, 81.8, 82.9],
}
r = requests.post("https://api.paperbanana.me/api/plot", headers=HEADERS, json={
"data_json": json.dumps(data),
"intent": "Bar chart comparing model accuracy with value labels",
"iterations": 3,
"aspect_ratio": "4:3",
"output_formats": ["png", "svg", "pdf"],
"vlm_provider": "apimart",
"vlm_model": "gemini-2.5-flash",
})
print(r.json())
拼接多图组合图
POST/api/composite
将多个已完成任务的输出图片拼接成一张 PNG 组合图,并可选添加子图标签。 这个接口只做服务端本地图片处理,不会再触发一次上游模型调用。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
source_task_ids | array[string] | 是 | 至少两个已完成的任务 ID,且必须属于同一个 API Key |
layout | string | 否 | 网格布局,格式为 RxC,如 1x3、2x2,或传 auto 自动排布。默认值为 auto |
labels | array[string] | 否 | 可选的显式子图标签。不传时会自动生成 (a)、(b)、(c) 等标签 |
spacing | integer | 否 | 子图之间以及边缘留白的像素间距。默认值为 20 |
label_position | string | 否 | 标签绘制位置。支持 top、bottom。默认值为 bottom |
label_font_size | integer | 否 | 子图标签字体大小。默认值为 32 |
webhook_url | string | 否 | 可选回调地址,用于接收最终结果 |
webhook_include_image | boolean | 否 | 若为 true,webhook 载荷中会包含 image_base64 |
响应 202 Accepted
{
"task_id": "c0ffee123456...",
"status": "pending"
}
source_task_ids 中的每个任务都必须已经处于 completed 状态,
必须归属于当前 API Key,并且对应图片文件仍然存在。
curl -X POST https://api.paperbanana.me/api/composite \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"source_task_ids": ["task_a", "task_b", "task_c"],
"layout": "1x3",
"labels": ["(a)", "(b)", "(c)"],
"spacing": 20,
"label_position": "bottom",
"label_font_size": 32
}'
const res = await fetch("https://api.paperbanana.me/api/composite", {
method: "POST",
headers: {
"Authorization": "Bearer pb_your_api_key_here",
"Content-Type": "application/json"
},
body: JSON.stringify({
source_task_ids: ["task_a", "task_b", "task_c"],
layout: "1x3",
labels: ["(a)", "(b)", "(c)"],
spacing: 20,
label_position: "bottom",
label_font_size: 32
})
});
console.log(await res.json());
import requests
r = requests.post("https://api.paperbanana.me/api/composite", headers=HEADERS, json={
"source_task_ids": ["task_a", "task_b", "task_c"],
"layout": "1x3",
"labels": ["(a)", "(b)", "(c)"],
"spacing": 20,
"label_position": "bottom",
"label_font_size": 32,
})
print(r.json())
Webhook 回调
如果你不想轮询任务状态,可以提供 webhook_url,
当任务成功完成或失败时,PaperBanana 会主动发送 HTTP POST 回调。
配置层级
Webhook 支持两个层级:
| 层级 | 配置方式 | 优先级 |
|---|---|---|
| API Key 默认配置 | 创建或更新 API Key 时设置 webhook_url 和可选的 webhook_secret |
当请求体未指定 webhook URL 时使用 |
| 单次请求覆盖 | 在 /api/generate、/api/optimize-prompt、/api/plot 或 /api/composite 请求体中传入 webhook_url |
覆盖该次任务的默认配置 |
generate / optimize-prompt / plot / composite 请求中的 webhook 字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
webhook_url | string | 否 | 任务结束后接收结果的回调地址 |
webhook_include_image | boolean | 否 | 若为 true,回调载荷中包含 image_base64 |
Webhook 载荷
POST https://your-server.com/paperbanana
Content-Type: application/json
X-PaperBanana-Event: task.completed
X-PaperBanana-Signature: sha256=a1b2c3d4...
User-Agent: PaperBanana-Webhook/0.1.2
{
"event": "task.completed",
"task_id": "a1b2c3d4e5f6...",
"status": "completed",
"diagram_type": "methodology",
"description": "The diagram shows...",
"image_url": "https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image",
"image_base64": null,
"error": null,
"created_at": "2026-02-15T10:30:00+00:00",
"completed_at": "2026-02-15T10:31:25+00:00"
}
webhook_include_image 为 true 时,
image_base64 才会被填充。否则请使用 image_url 并携带原始 Bearer 令牌下载图片。对于纯文本的提示词优化任务,image_url 会是 null。
签名校验
如果该 API Key 配置了 webhook_secret,
回调请求会带上 X-PaperBanana-Signature,
它是对原始请求体做 HMAC-SHA256 签名后的结果。
import hmac
import hashlib
def verify_signature(body: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(
secret.encode(), body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
const crypto = require("crypto");
function verifySignature(body, signature, secret) {
const expected = "sha256=" + crypto
.createHmac("sha256", secret)
.update(body)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signature)
);
}
重试策略
如果因为网络错误或 HTTP 状态码大于等于 400 导致投递失败,PaperBanana 最多会重试三次:
| 尝试 | 下一次重试前的延迟 |
|---|---|
| 第 1 次重试 | 5 秒 |
| 第 2 次重试 | 15 秒 |
| 第 3 次重试 | 60 秒 |
你可以在管理界面的历史记录中查看 webhook 投递状态。
带 webhook 的请求示例
curl -X POST https://api.paperbanana.me/api/generate \
-H "Authorization: Bearer pb_your_api_key_here" \
-H "Content-Type: application/json" \
-d '{
"source_context": "Our model uses a 6-layer Transformer encoder...",
"caption": "Figure 1: Transformer architecture",
"iterations": 3,
"webhook_url": "https://your-server.com/pb-callback",
"webhook_include_image": false
}'
import requests
r = requests.post("https://api.paperbanana.me/api/generate", headers=HEADERS, json={
"source_context": "Our model uses a 6-layer Transformer encoder...",
"caption": "Figure 1: Transformer architecture",
"iterations": 3,
"webhook_url": "https://your-server.com/pb-callback",
"webhook_include_image": False,
})
print(r.json())
const res = await fetch("https://api.paperbanana.me/api/generate", {
method: "POST",
headers: {
"Authorization": "Bearer pb_your_api_key_here",
"Content-Type": "application/json"
},
body: JSON.stringify({
source_context: "Our model uses a 6-layer Transformer encoder...",
caption: "Figure 1: Transformer architecture",
iterations: 3,
webhook_url: "https://your-server.com/pb-callback",
webhook_include_image: false
})
});
console.log(await res.json());
查询任务状态与结果
GET/api/tasks/{task_id}
获取任务当前状态。任务完成后一定会返回最终的 description;只有会产出图片的任务才会返回图片相关字段。
路径参数
| 参数 | 类型 | 说明 |
|---|---|---|
task_id | string | 由 generate、optimize-prompt、plot 或 composite 返回的任务 ID |
响应 200
{
"task_id": "a1b2c3d4e5f6...",
"status": "completed",
"diagram_type": "statistical_plot",
"image_base64": "iVBORw0KGgo...",
"artifacts": {
"png": {
"format": "png",
"media_type": "image/png",
"download_url": "https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image"
},
"svg": {
"format": "svg",
"media_type": "image/svg+xml",
"download_url": "https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image?format=svg"
},
"pdf": {
"format": "pdf",
"media_type": "application/pdf",
"download_url": "https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image?format=pdf"
}
},
"description": "The diagram shows...",
"error": null
}
diagram_type = "prompt_optimization" 时,响应中仍会包含优化后的 description,但不会有 image_base64、artifacts 或可下载图片。
状态生命周期
| 状态 | 含义 | 下一步 |
|---|---|---|
pending | 任务已进入队列 | 继续轮询 |
running | 正在生成中 | 继续轮询 |
completed | 结果已完成 | 读取 image_base64 获取 PNG 预览,或查看 artifacts 下载各格式文件 |
failed | 任务失败 | 检查 error 字段 |
curl https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6 \
-H "Authorization: Bearer pb_your_api_key_here"
const res = await fetch("https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6", {
headers: { "Authorization": "Bearer pb_your_api_key_here" }
});
const task = await res.json();
console.log(task);
r = requests.get(
"https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6",
headers=HEADERS
)
task = r.json()
print(task)
下载任务图片
GET/api/tasks/{task_id}/image
下载任务产物文件。默认返回 PNG。对统计图任务,还可以通过 ?format=svg 或 ?format=pdf 下载向量文件。
提示词优化任务不会产出可下载图片。
响应 200
返回对应格式的二进制文件,Content-Type 会随请求格式变化。
响应 404
任务不存在,或图片当前尚不可用。
curl -o diagram.png https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image \
-H "Authorization: Bearer pb_your_api_key_here"
curl -o plot.svg "https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image?format=svg" \
-H "Authorization: Bearer pb_your_api_key_here"
const res = await fetch("https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image", {
headers: { "Authorization": "Bearer pb_your_api_key_here" }
});
const blob = await res.blob();
const url = URL.createObjectURL(blob);
window.open(url, "_blank");
r = requests.get(
"https://api.paperbanana.me/api/tasks/a1b2c3d4e5f6/image",
headers=HEADERS
)
r.raise_for_status()
with open("diagram.png", "wb") as f:
f.write(r.content)
创建 API Key
POST/api/admin/keys
创建一个新的 API Key,可附带名称和可选配额。需要管理员认证。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
name | string | 是 | 可读名称,例如 research-team 或 my-app |
quota | integer | 否 | 初始配额。不传则表示无限 |
webhook_url | string | 否 | 此 Key 对应任务的默认 webhook URL |
webhook_secret | string | 否 | 用于 webhook HMAC-SHA256 签名的密钥 |
响应 201 Created
{
"key": "pb_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
"name": "my-app",
"quota_remaining": 100,
"quota_total": 100,
"total_used": 0,
"active": true,
"created_at": "2026-02-15T10:30:00+00:00",
"webhook_url": "https://your-server.com/callback"
}
curl -X POST https://api.paperbanana.me/api/admin/keys \
-H "Authorization: Bearer YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "my-app", "quota": 100}'
const res = await fetch("https://api.paperbanana.me/api/admin/keys", {
method: "POST",
headers: {
"Authorization": "Bearer YOUR_ADMIN_KEY",
"Content-Type": "application/json"
},
body: JSON.stringify({ name: "my-app", quota: 100 })
});
console.log(await res.json());
ADMIN_HEADERS = {"Authorization": "Bearer YOUR_ADMIN_KEY"}
r = requests.post("https://api.paperbanana.me/api/admin/keys",
headers=ADMIN_HEADERS,
json={"name": "my-app", "quota": 100}
)
print(r.json())
API Key 列表
GET/api/admin/keys
返回所有 API Key 及其配额和使用情况。需要管理员认证。
响应 200
[
{
"key": "pb_a1b2c3...",
"name": "my-app",
"quota_remaining": 87,
"quota_total": 100,
"total_used": 13,
"active": true,
"created_at": "2026-02-15T10:30:00+00:00"
}
]
curl https://api.paperbanana.me/api/admin/keys \
-H "Authorization: Bearer YOUR_ADMIN_KEY"
const res = await fetch("https://api.paperbanana.me/api/admin/keys", {
headers: { "Authorization": "Bearer YOUR_ADMIN_KEY" }
});
console.log(await res.json());
r = requests.get("https://api.paperbanana.me/api/admin/keys", headers=ADMIN_HEADERS)
print(r.json())
API Key 详情
GET/api/admin/keys/{key}
返回 API Key 的完整信息,以及最近的使用日志。需要管理员认证。
路径参数
| 参数 | 类型 | 说明 |
|---|---|---|
key | string | 完整 API Key 字符串 |
响应 200
{
"key": "pb_a1b2c3...",
"name": "my-app",
"quota_remaining": 87,
"quota_total": 100,
"total_used": 13,
"active": true,
"created_at": "2026-02-15T10:30:00+00:00",
"usage_log": [
{
"id": 1,
"task_id": "abc123...",
"endpoint": "generate",
"created_at": "2026-02-15T11:00:00+00:00"
}
]
}
curl https://api.paperbanana.me/api/admin/keys/pb_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 \
-H "Authorization: Bearer YOUR_ADMIN_KEY"
api_key = "pb_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6"
r = requests.get(f"https://api.paperbanana.me/api/admin/keys/{api_key}", headers=ADMIN_HEADERS)
print(r.json())
更新 API Key
PATCH/api/admin/keys/{key}
更新配额、启用状态或 webhook 默认配置。请求体中的所有字段都是可选的。
请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
quota_remaining | integer | 否 | 设置剩余配额,最小为 0 |
quota_total | integer | 否 | 设置总配额,最小为 0 |
active | boolean | 否 | 启用或禁用该 Key |
webhook_url | string | 否 | 更新默认 webhook URL,传空字符串表示移除 |
webhook_secret | string | 否 | 更新 webhook 签名密钥,传空字符串表示移除 |
响应 200
接口会返回更新后的 API Key 对象。
curl -X PATCH https://api.paperbanana.me/api/admin/keys/pb_a1b2c3... \
-H "Authorization: Bearer YOUR_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{"quota_remaining": 150, "quota_total": 200, "active": true}'
r = requests.patch(
"https://api.paperbanana.me/api/admin/keys/pb_a1b2c3...",
headers=ADMIN_HEADERS,
json={"quota_remaining": 150, "quota_total": 200, "active": True}
)
print(r.json())
删除 API Key
DELETE/api/admin/keys/{key}
永久删除一个 API Key 及其使用日志。该操作不可撤销。
响应 204 No Content
删除成功时响应体为空。
curl -X DELETE https://api.paperbanana.me/api/admin/keys/pb_a1b2c3... \
-H "Authorization: Bearer YOUR_ADMIN_KEY"
r = requests.delete(
"https://api.paperbanana.me/api/admin/keys/pb_a1b2c3...",
headers=ADMIN_HEADERS
)
assert r.status_code == 204