教程
FLUX 3 Max API:在 hiapi 上使用文本生成视频、图片生成视频与关键帧
三个模型 ID,一条 /v1/tasks 流程:curl、Python 辅助脚本、关键帧时间轴规则,以及真实的校验错误。
摘要
- hiapi 上的 FLUX 3 Max 是同一个
POST /v1/tasks端点上的三个独立模型 ID:flux-3-max/text-to-video、flux-3-max/image-to-video和flux-3-max/keyframes-to-video。切换模式只需更改model和一个输入字段。 - 三者都能生成 5–20 秒、720p 或 1080p 的片段,按输出秒数和分辨率计费。实时价格请查看 /en/pricing。
- 关键帧的帧号规则在提交时不会被检查。时间轴有误时接口仍会返回 HTTP 200 和
taskId,但任务会在约 2 秒后失败。请先在本地校验。
本指南针对 Max 档。带草稿模式和视频续写的基础 flux-3 模型见 FLUX.3 视频 API 指南,对现有素材进行编辑见 flux-3 视频编辑。
前提条件
在 hiapi 控制台创建密钥并导出:
export HIAPI_KEY=sk-your-key
所有模式的请求流程相同:POST /v1/tasks 返回 taskId,然后轮询 GET /v1/tasks/<taskId>(或使用回调),直到 status 为 success,再下载 data.output[0].url。
不同输入对应哪个模型 ID
| 你拥有的输入 | 模型 ID | 需要更改的输入 |
|---|---|---|
| 只有文本提示词 | flux-3-max/text-to-video |
prompt |
| 一张静止图(可选再给一张尾帧) | flux-3-max/image-to-video |
image_url,可选 end_image_url |
| 1–10 张必须落在特定时刻的图片 | flux-3-max/keyframes-to-video |
keyframes: [{image_url, frame_index}] |
三者共用的可选字段:duration(整数秒,5–20,默认 5)、resolution(720p 或 1080p)、aspect_ratio(auto、21:9、2:1、16:9、4:3、1:1、3:4、9:16)、generate_audio(布尔值,默认开启)和 reasoning_level(low 或 high,默认 high)。模型页面:文本生成视频、图片生成视频、关键帧生成视频。
最小示例:用 curl 进行文本生成视频
curl -X POST "https://api.hiapi.ai/v1/tasks" \
-H "Authorization: Bearer $HIAPI_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "flux-3-max/text-to-video",
"input": {
"prompt": "a slow aerial shot of ocean waves at sunrise, photorealistic",
"duration": 5,
"resolution": "720p",
"generate_audio": false,
"reasoning_level": "low"
}
}'
请求有效时返回 {"code":200,"data":{"taskId":"tk-hiapi-..."},"message":"success"}。然后:
curl https://api.hiapi.ai/v1/tasks/tk-hiapi-... -H "Authorization: Bearer $HIAPI_KEY"
generate_audio 加上 5 秒 low 推理的配置,是比较提示词效果的廉价方式;模型页面说明音频不会改变价格。
一个 Python 辅助脚本覆盖全部三种模式
# pip install requests
import os, time, requests
KEY = os.environ["HIAPI_KEY"]
H = {"Authorization": f"Bearer {KEY}"}
BASE = "https://api.hiapi.ai/v1/tasks"
def submit(mode: str, **inp) -> str:
"""mode: text-to-video | image-to-video | keyframes-to-video"""
body = {"model": f"flux-3-max/{mode}", "input": inp}
res = requests.post(BASE, json=body, headers=H, timeout=30).json()
if res.get("code") != 200:
# sync 400s look like {"code":400,"error_code":"INVALID_REQUEST","message":"invalid input: ..."}
raise RuntimeError(res)
return res["data"]["taskId"]
def wait(task_id: str, timeout_s: int = 900, every: int = 10) -> str:
deadline = time.time() + timeout_s
while time.time() < deadline:
d = requests.get(f"{BASE}/{task_id}", headers=H, timeout=30).json()["data"]
if d["status"] == "success":
return d["output"][0]["url"]
if d["status"] == "fail":
raise RuntimeError(d.get("error"))
time.sleep(every)
raise TimeoutError(task_id)
# 1) text-to-video
t2v = submit("text-to-video",
prompt="A single white sneaker rotates slowly on a wet black plinth, studio softbox lighting, macro product shot",
duration=5, resolution="720p", aspect_ratio="16:9", generate_audio=False)
# 2) image-to-video: first frame, optional last frame
i2v = submit("image-to-video",
prompt="Morning mist drifts over the harbor; the boats rock gently and the camera slowly pushes forward.",
image_url="https://example.com/harbor.jpg",
# end_image_url="https://example.com/harbor-end.jpg", # optional fixed ending
duration=5, resolution="720p", generate_audio=False)
# 3) keyframes-to-video
FPS = 24
def keyframes_ok(duration: int, frames: list[dict]) -> None:
idx = [f["frame_index"] for f in frames]
assert 1 <= len(frames) <= 10, "1-10 keyframes"
assert len(set(idx)) == len(idx), "frame_index must be unique"
assert all(0 <= i <= duration * FPS for i in idx), f"frame_index must be 0..{duration * FPS}"
frames = [
{"image_url": "https://example.com/harbor.jpg", "frame_index": 0},
{"image_url": "https://example.com/latte.jpg", "frame_index": 120},
]
keyframes_ok(5, frames)
kf = submit("keyframes-to-video",
prompt="A continuous cinematic morph from the misty harbor into a close-up of an iced matcha latte.",
keyframes=frames, duration=5, resolution="720p", generate_audio=False)
url = wait(kf)
open("clip.mp4", "wb").write(requests.get(url).content)
输出 URL 会过期,所以任务成功后应立即下载或复制文件,而不是保存链接。
关键帧:时间轴如何运作
frame_index 是从 0 开始的 24 fps 时钟上的位置,而不是百分比。允许的最大值为 duration × 24:5 秒为 120,10 秒为 240,20 秒为 480。索引必须唯一,可传 1 到 10 个关键帧。要实现干净的 A 到 B 过渡,把第一个锚点放在 0,最后一个放在 duration × 24;把最后一个锚点提前可保持结尾,推后可压缩变化。保持所有关键帧的宽高比一致;如需无缝循环,让首尾图像看起来像同一时刻。
生产环境注意事项
- 轮询与回调。 任务 API 接受
callback.url和when: "final",这样就不必轮询;像上面那样每 10 秒轮询一次,对脚本和批处理任务来说也没问题。 - 始终给轮询循环设置超时,并在轮询前持久化
taskId,以免程序崩溃导致已付费的任务丢失。 - 立即下载。 将
output[0].url视为短期有效。 - 成本控制。 计费按输出秒数和分辨率计算,所以 20 秒 1080p 片段的成本是 5 秒 720p 测试的好几倍。先用 5 秒和 720p 做原型,提示词有效后再提升两者。当前费率见价格页面。
我测试了什么,没测试什么
这些检查于 2026 年 10 月 23 日使用只读测试密钥针对线上 API 进行。本文没有渲染完整片段,因此我不对输出质量做任何断言;示例提示词来自模型页面自带的示例。
同步校验(HTTP 400,未入队)。 超出范围的值在提交时即被拒绝,返回 error_code: INVALID_REQUEST 和可读的 message:
| 请求 | 响应 message |
|---|---|
duration: 3 |
invalid input: duration: minimum: got 3, want 5 |
resolution: "4k" |
value must be one of '720p', '1080p' |
aspect_ratio: "5:4" |
value must be one of 'auto', '21:9', '2:1', '16:9', '4:3', '1:1', '3:4', '9:16' |
reasoning_level: "medium" |
value must be one of 'low', 'high' |
图片生成视频使用 image_urls 而非 image_url |
image_url: missing required field "image_url"; ... additional properties 'image_urls' not allowed |
最后一行是常见错误:hiapi 其他图片输入模型使用复数字段,但 FLUX 3 Max 图片生成视频只接受单个 image_url。未知字段会被拒绝,而不是忽略。
关键帧时间轴错误是异步的。 我提交了两个 5 秒关键帧请求,时间轴都有问题:一个使用 frame_index: 121(超过 120 上限),另一个两个关键帧都在 frame_index: 0。两者都被接受,返回 HTTP 200 和 taskId。创建后约 1–2 秒轮询返回终态:
{"code":200,"data":{"status":"fail","model":"flux-3-max/keyframes-to-video",
"error":{"code":"INVALID_REQUEST","message":"Invalid request parameters for this model. Please check the request and try again."},
"taskId":"tk-hiapi-..."},"message":"success"}
由此可得两点。第一,提交返回 200 并不意味着时间轴有效,所以轮询时要检查 status 和 error.code。第二,失败消息很笼统,不会指出哪一帧有问题,这就是上面 keyframes_ok 检查值得在提交前运行的原因。模型页面说明失败任务不收费;我没有检查这两个任务的账单记录。
常见问题
首帧加尾帧应该用哪个模型 ID? 使用 flux-3-max/image-to-video,传入 image_url 和 end_image_url。只有需要在首尾之间设置锚点时才用关键帧。
增加关键帧会更贵吗? 根据模型页面,不会:计费按输出秒数和分辨率计算,锚点数量不影响价格。
这与 flux-3 有何区别? flux-3-max/* 是独立模型 ID,按模式拆分(文本、图片、关键帧);而基础 flux-3 是一个 ID,根据输入切换模式,还支持草稿模式和续写。参见 FLUX.3 视频 API 指南。我没有比较两者的输出质量,所以请根据功能和价格页面上的费率来选择。
可以改用其他图片生成视频模型吗? 可以。其他图片生成视频方案见 如何使用 Ray 3.2 图片生成视频 API。