Gemini 原生接口
Gemini 系列走 Google 自己的原生形状,路径 POST /v1beta/models/{别名}:generateContent。视频理解就在这里。
Gemini 别名不能填进 /v1/chat/completions 或 /v1/messages——那样会拿到:
{"error":{"code":"INVALID_REQUEST","message":"this endpoint requires Gemini non-streaming contents","request_id":"req_01M2M7ERGYAKQ5VHZTJ104RH4J"}}
别名
| 别名 | 说明 |
|---|---|
gemini-3.1-pro-preview |
当前主力 |
gemini-2.5-pro |
上一代 Pro |
gemini-3-pro-preview |
兼容别名,实际返回 3.1 Pro |
最短的一次调用
curl "https://api.apipace.com/v1beta/models/gemini-2.5-pro:generateContent" \
-H "x-api-key: $APIPACE_KEY" \
-H "content-type: application/json" \
-H "Idempotency-Key: docs-gemini-20260916-b" \
-d '{
"contents": [{"role":"user","parts":[{"text":"用一句话介绍杭州"}]}],
"generationConfig": {"maxOutputTokens": 256}
}'
{
"candidates": [{
"content": {"role":"model","parts":[{"text":"杭州,一座浸润在西…"}]},
"finishReason": "MAX_TOKENS"
}],
"modelVersion": "gemini-2.5-pro",
"responseId": "qheqapWYGo2x_9MP25uM6As",
"usageMetadata": {
"promptTokenCount": 5,
"candidatesTokenCount": 7,
"thoughtsTokenCount": 245,
"totalTokenCount": 257
}
}
返回是原生 Gemini 形状,没有额外的平台字段——平台信息全在响应头里(见下)。
thoughtsTokenCount是思考 token,它按输出计费。上面那次只输出 7 个可见 token,却花了 245 个思考 token;maxOutputTokens给小了会像第一次那样直接MAX_TOKENS截断,而思考 token 已经产生。给 Gemini Pro 留够maxOutputTokens。
Idempotency-Key 是必填的
这是这个接口和其它接口最不一样的地方:没有 Idempotency-Key 直接 400。
{"error":{"code":400,"status":"INVALID_REQUEST","execution_state":"not_started",
"message":"a stable Idempotency-Key of at most 128 characters is required",
"request_id":"req_01M2M7ETJP0BDQCVVTAZ7JPB9N"}}
原因是这些请求可能很贵(一段视频轻易就是几十万 token),而网络会断。平台用这个键保证:同一个键 + 同样的正文,最多只会真的生成一次。
- 同键同正文重放 → 返回原结果,响应头带
x-p34-replayed: true,不再扣一次钱。 - 同键但正文变了 → 冲突,拒绝。
- 键用过了就不会因为超时或过期而重新开放,不存在「换个时间重试同一个键会再买一次」。
所以这个键要稳定:按你业务里那个任务的 ID 生成,别用随机数,也别每次重试换一个。
四个响应头
| 头 | 值 | 怎么用 |
|---|---|---|
x-p34-execution-state |
not_started / completed / unknown |
见下 |
x-p34-usage-state |
reported / unknown |
unknown = 用量没拿到,这一单不扣钱 |
x-p34-charged-micro-cny |
整数微元 | 这一单扣了多少 |
x-p34-replayed |
true |
这次返回来自重放,不是新生成 |
x-p34-execution-state 是断线之后决定「能不能重来」的唯一依据:
not_started:请求根本没发出去,可以换个新键重来。completed:已经跑完了。unknown:可能跑了也可能没跑。不要新建调用,拿原来的键去查(见下一节)。
结果找回
拿你提交时用的那个 Idempotency-Key 去查——注意路径里填的是这个键,不是 request_id:
curl "https://api.apipace.com/v1beta/invocations/docs-video-20260916-a" \
-H "x-api-key: $APIPACE_KEY"
返回原生成结果,响应头带 x-p34-replayed: true。这个查询不发起生成、不扣费、不要求余额,同租户同 key 才能读。
返回 409 表示:还在跑、结果确认不了、或者正文超过 1 MiB 没法重放。409 本身不表示可以新建调用。
视频理解
视频用原生的 inline_data 传,mime_type 写视频的真实类型:
python3 - <<'PY' > body.json
import base64, json
v = base64.b64encode(open("clip.mp4","rb").read()).decode()
json.dump({"contents":[{"role":"user","parts":[
{"inline_data":{"mime_type":"video/mp4","data":v}},
{"text":"这段视频里画面的颜色先后是什么?只答颜色。"}]}],
"generationConfig":{"maxOutputTokens":512}}, open("/dev/stdout","w"))
PY
curl "https://api.apipace.com/v1beta/models/gemini-2.5-pro:generateContent" \
-H "x-api-key: $APIPACE_KEY" -H "content-type: application/json" \
-H "Idempotency-Key: clip-2026-09-16-0001" \
--data-binary @body.json
返回里 promptTokensDetails 会按模态拆开,视频占了多少一目了然:
{
"candidates": [{"content":{"role":"model","parts":[{"text":"红色"}]},"finishReason":"STOP"}],
"modelVersion": "gemini-2.5-pro",
"usageMetadata": {
"promptTokenCount": 271,
"promptTokensDetails": [{"modality":"TEXT","tokenCount":13},{"modality":"VIDEO","tokenCount":258}],
"candidatesTokenCount": 1,
"thoughtsTokenCount": 405,
"totalTokenCount": 677
}
}
上面这一发是一段 1 秒、128×128 的测试片,视频部分 258 token,整单扣 30,791 微元 = ¥0.030791(思考 token 是大头)。真实素材的 token 数大致随时长线性增长,先用一小段试出单价再批量跑。
限制
| 项 | 限制 |
|---|---|
| 请求体 | ≤ 96 MiB。没有 Files API,大文件要么压,要么切段 |
| 输出 | 只有文本、单候选 |
| 不支持 | tools、显式上下文缓存、Files、流式 |
| 长上下文 | prompt 超过 200,000 token 走长上下文档价目 |
| 预占 | 带视频的请求按模型最大输入量(1,048,576 token)加 maxOutputTokens 预占,最终按实际用量结算。所以余额要够预占,而不是够实际花费 |
计费
按 usageMetadata 里的实际用量结算:promptTokenCount 计输入,candidatesTokenCount + thoughtsTokenCount 计输出。缓存命中的部分也一并折进输入。
用量拿不到时(x-p34-usage-state: unknown)结果照给、不扣钱,平台不按媒体字节数猜 token。这类调用会计进 GET /v1/user/statement 的 unconfirmed_calls,之后也不会偷偷追扣。
单价见 https://api.apipace.com/pricing,或 GET /v1/pricing(免鉴权)里 capabilities 含 gemini_native 的那几条。
错误信封
这个接口用的是 Google 原生形状,和 /v1 的信封不一样:
{"error":{"code":400,"status":"INVALID_REQUEST","execution_state":"not_started",
"message":"…","request_id":"req_…"}}
code 是 HTTP 数字,status 才是字符串码。多一个 execution_state,语义同响应头。5xx 表示执行结果未确认——只查原调用,不要自动再买一次。