Pika API 的接口目录是公开的、不需要 Key。文档特别强调:目录和每个接口的规格是运行时的事实来源,不要凭模型名去猜接口路径、字段和取值。本篇写一个目录浏览器:列出 Pika 的全部接口,查看某个接口的参数(类型、必填、默认值、取值范围),一键生成 Markdown 速查表。
先看清成品(全部是 box 上对真实公开接口的调用)
- 测试: pytest 1 项通过。
- 列出接口: 目录里共 176 个接口,其中 Pika 自家的 14 个。
- 查看 Pikaframes(关键帧)参数: images 1–5 张,transition_duration_s 1–10 秒。
- 速查表: 一次生成了 14 个接口的速查表。
- 价格: 默认不输出价格(价格会变)。
合规提醒:
PIKA_API_KEY和 webhook 签名密钥只放在服务器端,不要写进浏览器代码、URL、截图或聊天里(Pika 文档原话也是这样要求的)。输入的图片和视频必须是你有权使用的,不要用他人肖像冒充真人,不要侵犯版权或商标。生成的内容发布时要按《人工智能生成合成内容标识办法》加上“AI 生成”标识,并遵守 Pika 的使用条款。Pika API 是预付费的,没有免费的生成额度,也没有测试环境,每次生成都按实时报价扣费。本文不列价格,示例输出里的金额已用“x.xx”代替,请以运行时的报价为准。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
pika_catalog.py |
list(按厂商、类别筛选)/ show(参数表,加 --price 显示带查询时间的价格)/ sheet(Markdown 速查表) |
test_catalog.py |
参数表格式 |
需要准备哪些环境?
Python 3.9 以上,pip install requests pytest。不需要 Key。
文件结构
48-pika-A-src/
├── pika_catalog.py
└── test_catalog.py
第 1 步:代码
pika_catalog.py:
"""浏览 Pika API 的公开目录(不需要 API Key):列出某个厂商或类别的接口,查看某个接口的输入参数,生成一份 Markdown 速查表。
文档(dev.pika.art/llms.txt):目录和每个接口的规格是“运行时的事实来源”,不要凭模型名猜接口路径、字段或取值。
- GET https://api.dev.pika.art/catalog/apis 全部接口(免费调用)
- GET https://api.dev.pika.art/catalog/apis/{api_id}?expand=inputs 单个接口,带 input_schema(JSON Schema)
价格字段(display_pricing)会随时变化:本脚本默认不输出价格,加 --price 才显示,并注明查询时间。
用法:python pika_catalog.py list [--vendor pika] [--category video]
python pika_catalog.py show pika/pika-2.5/image-to-video [--price]
python pika_catalog.py sheet --vendor pika --out pika接口速查.md"""
import argparse, json, sys
from datetime import datetime
import requests
API = "https://api.dev.pika.art"
def get(path, **params):
r = requests.get(API + path, params=params, timeout=30)
if r.status_code != 200: sys.exit(f"✗ HTTP {r.status_code}:{r.text[:200]}")
return r.json()
def list_apis(vendor=None, category=None):
return [a for a in get("/catalog/apis")["apis"] if (not vendor or a["vendor"] == vendor) and (not category or a["category"] == category)]
def inputs(api_id):
"""返回 [(参数名, 类型, 是否必填, 默认值, 可选值/范围, 说明)]"""
s = get(f"/catalog/apis/{api_id}", expand="inputs")
sch = s.get("input_schema") or {}; req = set(sch.get("required", []))
rows = []
for name, p in (sch.get("properties") or {}).items():
typ = p.get("type") or "/".join(x.get("type", "?") for x in p.get("anyOf", []) if x.get("type") != "null")
enum = p.get("enum") or next((x.get("enum") for x in p.get("anyOf", []) if x.get("enum")), None)
rng = [f"{k}={p[k]}" for k in ("minimum", "maximum", "minItems", "maxItems", "maxLength") if k in p] # 只看 enum 会漏掉范围限制
if "pattern" in p.get("items", p): rng.append(f"格式 {p.get('items', p)['pattern']}")
rows.append((name, typ, name in req, p.get("default"), (list(enum) if enum else []) + rng, (p.get("description") or "").replace("\n", " ")))
return s, rows
def price_lines(s):
out = []
for c in (s.get("display_pricing") or {}).get("components", []):
unit = c.get("unit", {})
for t in c.get("price_tiers", []):
out.append(f"{json.dumps(t.get('spec', {}), ensure_ascii=False)}:{t.get('sell_usd')} 美元 / {unit.get('quantity', 1)} {unit.get('type', '')}")
return out
def table(rows):
lines = ["| 参数 | 类型 | 必填 | 默认 | 可选值 / 范围 | 说明 |", "| :--- | :--- | :--- | :--- | :--- | :--- |"]
for n, t, r, d, e, desc in rows:
lines.append(f"| `{n}` | {t} | {'是' if r else ''} | {'' if d is None else d} | {', '.join(map(str, e)) if e else ''} | {desc} |")
return "\n".join(lines)
def main():
ap = argparse.ArgumentParser(); ap.add_argument("cmd", choices=["list", "show", "sheet"]); ap.add_argument("api_id", nargs="?")
ap.add_argument("--vendor"); ap.add_argument("--category"); ap.add_argument("--price", action="store_true"); ap.add_argument("--out")
a = ap.parse_args()
if a.cmd == "list":
apis = list_apis(a.vendor, a.category)
for x in apis: print(f"{x['api_id']:<45} {x['category']:<6} {x['call']['method']} {x['call']['path']}")
print(f"共 {len(apis)} 个")
elif a.cmd == "show":
s, rows = inputs(a.api_id); print(f"# {s['name']}\n{s.get('description', '')}\n调用:{s['call']['method']} {API}{s['call']['path']}\n\n{table(rows)}")
if a.price: print(f"\n价格({datetime.now():%Y-%m-%d %H:%M} 查询,以官网为准):\n" + "\n".join(price_lines(s)))
else:
md = [f"# Pika API 接口速查({a.vendor or '全部'})", "", f"- 生成时间:{datetime.now():%Y-%m-%d %H:%M},数据来自公开目录 {API}/catalog/apis", "- 价格会变化,本表不列价格;提交前用 quote 接口报价(见 48-pika-B)", ""]
for x in list_apis(a.vendor, a.category):
s, rows = inputs(x["api_id"])
md += [f"## {s['name']}(`{x['api_id']}`)", "", f"`{s['call']['method']} {s['call']['path']}`", "", table(rows), ""]
open(a.out, "w", encoding="utf-8").write("\n".join(md)); print(f"✓ 已写入 {a.out}({len(list_apis(a.vendor, a.category))} 个接口)")
if __name__ == "__main__":
main()
第 2 步:运行
$ python pika_catalog.py list --vendor pika
pika/pika-2.5/image-to-video video POST /v1/media/pika/pika-2.5/image-to-video
pika/pika-2.5/text-to-video video POST /v1/media/pika/pika-2.5/text-to-video
pika/pikadditions/video-to-video video POST /v1/media/pika/pikadditions/video-to-video
…(共 14 行)
pika/dynamic-captions/caption-video video POST /v1/media/pika/dynamic-captions/caption-video
共 14 个
$ python pika_catalog.py show pika/pikaframes/image-to-video
# Pika 2.5 Keyframe
Animate 1-5 ordered keyframes with individual transition prompts, durations, and optional looping. A single image requires looping.
调用:POST https://api.dev.pika.art/v1/media/pika/pikaframes/image-to-video
| 参数 | 类型 | 必填 | 默认 | 可选值 / 范围 | 说明 |
| :--- | :--- | :--- | :--- | :--- | :--- |
| `resolution` | string | | 1080p | 720p, 1080p | Output resolution. |
| `images` | array | 是 | | minItems=1, maxItems=5, 格式 ^https?:// | 1-5 ordered keyframe image URLs. A single image requires loop=true. |
| `transition_duration_s` | integer | | 5 | minimum=1, maximum=10 | Default seconds per transition. Multiple transitions are limited to 5 seconds each. |
| `loop` | boolean | | False | | Add a closing transition from the last image to the first. |
…
$ python pika_catalog.py sheet --vendor pika --out pika接口速查.md
✓ 已写入 pika接口速查.md(14 个接口)
检查点:注意 images 的说明“A single image requires loop=true”,以及 transition_duration_s 的“Multiple transitions are limited to 5 seconds each”。这两条是跨字段的规则,Schema 本身表达不了,B 篇会专门处理。
调试示例:只看 enum,漏掉了范围限制
第一版参数表的“可选值”一列只读取 enum。查看 Pikaframes 时,images 和 transition_duration_s 这两列都是空的,看起来像是“随便填”。打印原始 schema 才发现,限制写在别的字段里:
"images": {"type": "array", "minItems": 1, "maxItems": 5, "items": {"type": "string", "pattern": "^https?://"}},
"transition_duration_s": {"type": "integer", "minimum": 1, "maximum": 10, "default": 5}
现在 minimum、maximum、minItems、maxItems、maxLength 和 pattern 都会显示出来。另外,可以为空的字段在 schema 里写成 anyOf: [{"type": "string"}, {"type": "null"}],代码会把它显示为 string。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 接口列表和文章里的不一样 | 目录是实时的 | 以运行结果为准 |
| 价格和上周不同 | 价格会变 | 用 --price 查询时会注明查询时间;提交前报价(B 篇) |
| 参数说明是英文 | 目录原文就是英文 | 速查表保留原文,避免翻译出错 |
模板规则
- 接口路径和参数以目录为准,不凭印象写。
- 速查表要注明生成时间。
- 价格不写进文档,提交前实时报价。
项目资源
| 用途 | 地址 |
|---|---|
| Pika API 首页(llms.txt) | https://dev.pika.art/llms.txt |
| Pika API 开发者站点 | https://dev.pika.art/ |
| 公开目录(JSON) | https://api.dev.pika.art/catalog/apis |
| Pikaframes 规格 | https://dev.pika.art/llms/pika/pikaframes/image-to-video |
先查清楚目录,再写调用代码,Pika 的文档也是这么要求的。