前阵子要在自己的应用里接入 AI 视频生成能力我本以为最难的是“选哪个模型”结果打开三家视频生成 API 的文档发现光是“把请求发出去”这件事就能消耗掉一个下午A 平台要求x-api-keyB 平台要求Authorization: BearerC 平台要求把密钥放在 query 参数里请求体字段一个叫prompt另一个叫text还有一个叫input返回结果有的直接给视频 URL有的只给一个 task_id 让你轮询。不同平台的错误码也各成体系一个 400 背后的原因可能差了十万八千里。这种时候OpenRouter 视频生成 API 就会显得很有吸引力它用一套相对统一的协议帮你接多家模型把密钥、模型路由、计费和大部分错误处理收敛到一个入口。但“聚合”不是万能药它只是把“接入多家模型”这件事变成“接入一个网关”后面还有配额、超时、异步任务、内容合规和工程化落地这些硬问题。这篇文章我想从代码接入的角度按“先跑通、再处理异常、最后工程化”的顺序把整个过程拆开讲一遍。1. 先搞清这类 API 聚合平台到底帮你省了什么1.1 为什么多模型接入会变成一场适配噩梦如果你只接一个模型直接看那一家文档就够了。真正麻烦的是你要在同一个产品里比较两家、三家的视频生成效果或者你想做一个“用户可以选不同模型”的功能。这时候每个模型的接入方式不同会带来两倍的重复劳动。我经历过一个很典型的场景上游供应商临时说某个模型要下线我需要快速切到另一个模型。如果是直连我得重新读文档、改鉴权头、改请求体字段、改响应解析逻辑、重新测试。如果是走 OpenRouter 这类网关大部分时候只需要换一个model参数其他代码可以保持不变。这个“改动成本”的差距才是聚合平台最核心的价值。但这里要说清楚OpenRouter 并不是把每个模型的能力都统一成完全相同的样子。视频生成模型天然存在差异有的支持图生视频有的只支持文生视频有的限制了视频时长有的必须异步轮询。聚合层可以把“请求如何鉴权、如何计费、如何返回标准错误”统一起来但不可能把模型的底层能力差异也抹平。1.2 OpenRouter 的“代码优先”意味着什么“代码优先”不是官方术语是我自己更偏爱的一种接入姿势不做太多图形界面配置先用 curl 调通一次请求再用 Python 封装成函数最后再接入业务逻辑。这种姿势的好处是每一步都能被版本管理、被测试、被回滚。从实际使用看OpenRouter 的 API 风格接近 OpenAI 的 chat completions 协议这让很多已经写过 OpenAI 接口的开发者上手非常快。代码里你需要的核心要素就三样接口地址、API Key、模型名。其他都是围绕这三个要素的参数和数据格式。需要注意的是OpenRouter 聚合的是“能通过 API 访问的模型”如果你在模型列表里没有看到视频生成相关模型那可能是账号权限、地区或模型上架情况导致的。接入前一定要先打开官方模型列表确认而不是凭热搜词里的“MiniMax H3”“DeepSeek V4”等名字直接写进代码。2. 接入前必须确认的三件事账号、额度、模型列表2.1 注册、API Key 和充值的通用路径OpenRouter 的注册流程和大多数开发者平台类似打开官网注册账号进入控制台后创建 API Key。这个 Key 是你调用所有模型的统一凭证和直连各平台时的“多把钥匙”相比确实方便但也意味着一旦泄露别人可能拿着它去调用你账号下的所有模型。所以我建议不要把 API Key 硬编码在代码里使用环境变量。在.env文件中保存 Key并确保该文件被.gitignore忽略。创建 Key 时如果平台支持权限范围或额度限制尽量开启。至于充值OpenRouter 很多模型是按量计费的视频生成模型通常比文本模型更贵。如果你只是测试先充一小笔钱不要一开始就开大额自动充值。不同模型的价格、计费单位按秒还是按次都可能不一样具体以模型卡片和官方文档为准。2.2 怎么判断一个模型是否支持视频生成OpenRouter 的模型列表页一般会提供每个模型的说明、标签和示例。想找视频生成模型可以先搜索video、gen等关键词。真正的判断标准不是名字里有没有“video”而是模型卡片里是否明确写了输入输出支持视频输入是否支持prompt、image_url、duration、resolution等字段。输出返回video_url、video_data还是只返回文字描述。是否异步视频生成通常耗时较长如果响应里带task_id说明需要轮询。举个例子热搜词里出现过 MiniMax H3 在 ComfyUI 里生成视频时如何保持人物 ID 不变的问题。如果你真的想用某个模型做图生视频、保持人物一致性不要只看它“能不能生成视频”还要关注它支不支持输入参考图、支持多少张、以及视频时长上限。这些信息只能从模型文档里确认OpenRouter 自身不一定会在统一请求层帮你补齐。2.3 把 Key 放进环境变量而不是硬编码下面是一个常见的.env示例OPENROUTER_API_KEYsk-or-xxxx在 Python 里读取import os API_KEY os.environ[OPENROUTER_API_KEY]这样做的理由很简单代码一旦提交到仓库密钥就相当于公开了。很多人被自动抓取 GitHub 的爬虫扫到 Key然后发现账单暴涨问题往往就是硬编码造成的。3. 用 curl 跑通第一个视频生成请求3.1 先搭一个最小请求体我不建议一开始就去看复杂参数。先构造一个最简单的请求能返回结果就行。下面是一个用 curl 调用的示意结构curl -X POST https://openrouter.ai/api/v1/chat/completions \ -H Authorization: Bearer $OPENROUTER_API_KEY \ -H Content-Type: application/json \ -d { model: some-provider/video-model, messages: [ {role: user, content: 生成一段10秒的短视频主题是城市夜景} ] }注意上面的model是占位符具体模型 ID 以 OpenRouter 控制台里实际显示的为准。如果该模型在 OpenRouter 上使用的是独立的视频生成端点那么请求 URL 也可能不同一切以官方文档为准。这里要解释一下视频生成模型可能也复用 chat completions 格式因为这种格式可以传递文本指令也可能有专门的/video/generations端点。无论哪种Minimal Request 的原则是一样的先不要加thinking_budget、resolution、duration这些扩展参数减少变量跑通后再逐步加。3.2 识别同步响应和异步任务视频生成和文本生成最大的区别在于一个 HTTP 请求很难等完整个视频渲染过程。所以大概率会遇到两种响应模式同步模式请求一直挂起直到视频生成完毕响应中直接包含video_url。异步模式请求很快返回响应中包含一个任务 ID例如task_id你需要轮询另一个状态接口直到任务完成。如果你看到响应里返回了一个 URL先判断它是不是最终视频地址。有些平台会先返回一个“占位”任务 URL需要等状态变为 succeeded 之后才能真正访问。假设是异步任务轮询接口的示意结构类似curl -X GET https://openrouter.ai/api/v1/video/generations/{task_id} \ -H Authorization: Bearer $OPENROUTER_API_KEY轮询时不要每 0.5 秒就请求一次太密集容易触发速率限制也会给平台造成不必要的压力。常见做法是 2 到 5 秒一次配合最大轮询次数。3.3 第一次跑通后的检查清单第一次请求返回 200 并不代表完事。我一般会按这个清单检查HTTP 状态码是 200/201还是 2xx 代表已接受响应体有没有error字段有没有id或task_id视频文件如果不是直接给 URL而是给 base64需要确认体积别超过内存限制。视频可访问性URL 是否过期是否需要鉴权才能访问计费字段有些响应当中会带cost可以用于核对本次调用的费用。记录下请求时间、模型、任务 ID、状态码和耗时这些信息在后续调试时非常关键。4. 把 curl 封装成可复用的 Python 函数4.1 用 requests 写一个最小的视频生成函数一旦 curl 跑通就可以用 Python 固化。这里我用requests举例因为它足够简单也容易替换成httpx或异步客户端。import os import time import requests API_KEY os.environ[OPENROUTER_API_KEY] BASE_URL https://openrouter.ai/api/v1/chat/completions headers { Authorization: fBearer {API_KEY}, Content-Type: application/json, } def generate_video(prompt: str, model: str some-provider/video-model) - dict: payload { model: model, messages: [{role: user, content: prompt}], } response requests.post(BASE_URL, headersheaders, jsonpayload, timeout30) response.raise_for_status() return response.json()这里一个很容易踩的坑是timeout。视频生成请求可能比普通聊天请求慢很多但也不能因此不设超时否则代码会无限挂起。更合理的做法是把超时设置得比你的心理预期大一些比如 30 秒同时依赖异步任务机制而不是想着一个请求等到视频渲染完。4.2 轮询任务状态与结果下载如果响应里包含task_id就需要写一个轮询函数def poll_generation(task_id: str, max_attempts: int 60, interval: int 5) - dict: status_url fhttps://openrouter.ai/api/v1/video/generations/{task_id} for attempt in range(max_attempts): response requests.get(status_url, headersheaders, timeout10) data response.json() status data.get(status) if status succeeded: return data if status failed: raise RuntimeError(data.get(error, generation failed)) time.sleep(interval) raise TimeoutError(ftask {task_id} timed out)拿到结果后如果里面是视频 URL可以用requests.get(video_url, streamTrue)下载def download_video(url: str, save_path: str) - None: with requests.get(url, streamTrue, timeout30) as response: response.raise_for_status() with open(save_path, wb) as f: for chunk in response.iter_content(chunk_size8192): f.write(chunk)下载后可以用os.path.getsize(save_path)检查文件大小避免下到一个空文件或错误页。4.3 不要忽略异常处理与请求日志视频生成 API 的失败率往往比文本模型高因为它涉及计算资源调度、长时间任务、冷启动等。如果只依赖raise_for_status()一旦上游返回 529 或连接中断调用方只会看到一堆异常堆栈。一个可参考的做法是在调用前记录一条日志包含模型、prompt 长度、请求时间调用后记录 task_id、状态、耗时、费用如果有异常时记录错误类型和 response body。但要注意不要把完整 prompt 写入日志尤其当 prompt 包含用户隐私或业务敏感信息时。建议只记录 prompt 长度或摘要。日志示例: [INFO] generate_video start modelsome-provider/video-model prompt_len42 ts... [INFO] generate_video task_created task_idxxx statuspending [INFO] generate_video success task_idxxx duration12.3 cost0.0002 [ERROR] generate_video failed task_idxxx error529 overloaded这才是代码接入里真正值钱的部分不是调用成功而是失败时你能快速定位是哪一层出了问题。5. 视频生成 API 的典型错误和绕过思路5.1 529 overloaded 是什么意思很多 OpenRouter 使用者都遇到过api error: 529 overloaded. this is a server-side issue, usually temporary这样的报错。它表示服务端临时过载不是你请求参数写错了也不是 API Key 失效了。处理方式不是立刻重试一万次而是“后退重试”。常见做法是指数退避import time import random def request_with_retry(func, max_retries5, base_delay1): for attempt in range(max_retries): try: return func() except requests.HTTPError as exc: if exc.response.status_code 529 and attempt max_retries - 1: delay base_delay * (2 ** attempt) random.uniform(0, 1) time.sleep(delay) continue raise注意最大重试次数不要设得太高比如 5 到 6 次就够了。如果超过这个次数还在 529大概率是平台或模型提供方正在经历较大故障再继续重试只会浪费额度。5.2 connection lost mid-response 怎么办另一个常见错误是api error: connection lost mid-response. the response above may be incomplete。它意味着客户端和服务端之间的连接在响应过程中断开了。排查顺序应该是是不是网络不稳定比如公司网络、跨地域访问。是不是请求设置了过短的读取超时。模型生成时间较长网关在中间断开了连接。是否启用了流式输出而流式读取不稳定。对视频生成这种长任务我更推荐优先使用异步任务模式而不是同步等待。因为同步等待一旦连接断开你既拿不到结果也不知道任务是否还在后台运行状态变得不可控。异步任务至少能留下一个 task_id方便恢复查询。5.3 400 参数错误先看响应体再改代码400通常是请求体有问题比如热搜词里提到的thinking_budget parameter must be a positive integer就说明模型不支持这个非正整数的值。类似的错误还有上下文长度超限比如maximum context length is 1048576 tokens。处理这类参数错误时不要只看状态码要仔细读响应体里的error.message。OpenRouter 作为网关往往会把上游模型返回的原始错误信息透传出来这能帮你省很多事。如果确认是模型不支持的参数直接删除该参数如果是上下文超长就对输入做截断或摘要。这里有一个建议尽量把“请求参数构造”和“业务参数”分开。你在代码里定义自己的prompt、duration、resolution然后到一个适配层把业务参数转换成模型真正接受的参数。这样切模型时只需要改适配层而不是改所有业务代码。5.4 速率限制和费用控制除了平台可能限流模型提供方也可能有自己的配额。OpenRouter 统一了计费你可以在控制台看到调用记录和费用。但正因为“统一”你可能对每个模型的具体消耗没那么敏感。我的做法是测试阶段每个模型只跑少量样本先估算成本。生产环境设置单次请求的预检逻辑比如限制 prompt 长度、限制视频时长。如果响应带有cost字段记录到日志定期核对账单。下面是一个简化的问题排查表错误现象可能原因优先排查项处理建议401 UnauthorizedAPI Key 无效或缺失检查请求头中的 Authorization重新生成 Key确认没有多余空格400 Bad Request参数错误或模型不支持某字段读取响应体 error.message去掉不支持参数裁剪超长输入429 Too Many Requests触发速率限制检查近 1 分钟请求频率退避重试降低并发529 Overloaded服务端过载查看平台状态页指数退避必要时切换模型connection lost mid-response连接中断检查 timeout 和网络改用异步任务增加重试6. 适用边界什么场景适合用 OpenRouter 视频生成 API6.1 适合的人和团队OpenRouter 这类聚合 API 最适合以下场景快速原型验证你想比较三个视频生成模型的效果不想每家都注册一遍账号。内部工具给团队做一个“输入描述生成视频”的内部站点统一 API Key 和计费。个人开发者没有精力维护多家平台的 SDK希望用 OpenAI 风格接口快速接入。需要横跨不同模型做自动切换的自动化流程。在这些场景里统一协议带来的收益是实打实的代码结构基本一致切换模型成本低账单集中。6.2 不适合的场景聚合 API 不是银弹。下面这些场景我建议你谨慎考虑对延迟极度敏感聚合网关会引入额外一跳而且长任务受排队影响。数据必须留在内网视频渲染通常涉及大量数据如果合规要求数据不能出境那就不适合。需要深度定制模型行为某些模型的私有参数、特殊采样方式或者细粒度的回调不一定能在统一接口里完全暴露。超大批量任务如果每天要生成数万条视频聚合平台的费率和限流可能不如和模型提供方直接签合同划算。另外OpenRouter 本身也受上游模型服务条款约束。如果一个模型在特定地区不可用或者上线/下线状态有变化你可能会在某个时间点突然发现请求失败。所以不要把聚合平台当成“永不改变”的基础设施关键业务一定要有模型降级方案。6.3 关于“无限制”“免审核”的误区在热搜词里我留意到一些类似“无限制无审核生成视频”的说法。这里必须说清楚无论在哪个平台使用 AI 视频生成能力都要遵守平台服务条款、模型使用政策以及当地法律法规。所谓“无限制”“免审核”的软件很多时候要么是假的要么本身就是违规甚至违法的工具开发者一旦接入风险极高。即使 OpenRouter 作为聚合层帮你屏蔽了部分差异它也不会帮你规避内容安全责任。如果生成内容涉及侵权、色情、暴力、诈骗等黑灰产场景责任始终在调用方。这也是我为什么强调“代码优先”的另一层含义先把合规边界写进代码比如 prompt 预检、生成内容标记、用户举报机制而不是等出了事再补救。7. 当请求失败时按这个顺序排查7.1 先看现象和响应体遇到失败第一件事不是改代码而是记录现场。你需要确认HTTP 状态码是多少。响应体里的error字段写了什么。有没有request_id或id可以用于追踪。是第一次失败还是稳定复现。如果响应体里只有一句“internal server error”那大概率是平台侧问题如果详细说明了某个参数不合法那才是自己的问题。7.2 再查请求体和参数一旦确认是客户端问题重点检查这些项目model字符串是否和模型列表完全一致。messages或prompt字段是否为空、格式是否正确。是否传了模型不支持的额外字段。是否少传了必填字段比如图片输入时少了image_url。视频时长、分辨率是否超出模型限制。这里最容易让人困惑的是同一个请求换一个模型就能通过。这不是 OpenRouter 的问题而是模型之间的能力边界不同。遇到 400先对照该模型的文档做参数裁剪而不是盲目调整重试次数。7.3 然后查环境和网络如果请求代码本身没问题网络层也要排查。常见情况包括本机无法访问openrouter.ai可能需要检查 DNS 和网络连通性。公司防火墙或安全软件拦截了长连接。本地代理环境导致请求被路由到异常节点。容器部署时未正确设置网络代理或HTTPS_PROXY环境变量残留。排查时可以用一个最简单的文本模型接口测试如果文本模型接口正常视频生成接口失败那可能是视频生成服务的状态或参数问题如果连文本模型都失败那大概率是网络、Key 或账户问题。7.4 最后查账户、额度和模型状态这一步容易被忽略尤其是在“项目昨天还能跑今天突然不行”的时候API Key 是否过期或被重置。账户余额是否不足。是否触发了月度或分钟的速率限制。模型是否下线、暂停或切换了版本。是否因为内容审核策略命中被平台标记或限制。如果以上都没有问题那就把日志里记录的请求 ID 和错误信息发给平台支持而不是凭感觉“换个 Key 再试一次”。收尾从一次 API 接入到一套可复用流程写到这里你会发现这篇文章并没有给出某个具体视频模型的完整代码因为 OpenRouter 模型列表和接口细节是会变化的。真正值得沉淀的是一套接入思路第一步最小跑通。用 curl 发一个最简单的文字转视频请求确认鉴权、模型名、响应结构都正常不要在一开始就调一堆参数。第二步补齐异常处理。把 529、超时、参数错误、任务失败这些常见情况逐个写进代码让失败变得可观测、可恢复。第三步工程化落地。把 Key 放进环境变量把调用封装成函数把日志和计费记录接入你的监控体系再根据业务需求选择异步队列、并发控制和模型降级策略。这个框架不只适用于 OpenRouter也适用于任何视频生成 API。聚合平台能帮你省去重复适配的麻烦但真正决定一个功能能不能长期跑下去的是你对额度、错误、日志和合规边界的掌控。如果你正在准备接入建议现在就打开模型列表找一个支持视频生成的模型把第一段 curl 跑通。之后再看结果不迟。