查询任务
查询一个任务的状态和结果:返回、错误、任务对象和失败原因。
https://api.nanoapi.ai/v1/tasks/{task_id}查询一个任务的状态和结果。任务在后台生成;用了回调的话不用轮询,回调的请求体和这个接口的响应完全一样。所有响应都是 { code, message, data } 这一层外层,见响应格式。
curl https://api.nanoapi.ai/v1/tasks/tk_01jb8z5q4w3e2r1t0y9u8i7o6p \
-H "Authorization: Bearer $NANOAPI_API_KEY"返回
成功返回 200,data 是完整的任务对象:
{
"code": 200,
"message": "success",
"data": {
"object": "task",
"id": "tk_01jb8z5q4w3e2r1t0y9u8i7o6p",
"model": "genjutsu-1.0",
"status": "running",
"input": {
"mode": "motion_control",
"image_urls": ["https://example.com/person.png"],
"video_url": "https://example.com/dance.mp4",
"prompt": "",
"resolution": "720p"
},
"num_outputs": 1,
"outputs": [],
"billing": {
"currency": "USD",
"frozen": "4.095",
"charged": "0.00",
"refunded": "0.00",
"billed_seconds": null
},
"error": null,
"webhook_url": "https://example.com/hooks/nanoapi",
"created_at": "2026-10-22T08:30:00Z",
"started_at": "2026-10-22T08:31:05Z",
"finished_at": null,
"elapsed_seconds": null
}
}这是生成中的任务:outputs 还是空的,finished_at、elapsed_seconds 为 null,还没有扣费。成功的任务见任务对象,失败的见失败的任务。
data.status | 含义 | 接下来 |
|---|---|---|
queued / running | 排队中 / 生成中。响应带 Retry-After: 15,是建议的轮询间隔 | 15 秒以上再查 |
succeeded | 成功,data.outputs 是结果,data.billing.charged 是实际扣费 | 下载结果,见结果 |
failed | 任务失败了,不是这次查询出错。冻结的钱已全额退回 | 按 data.error.type 处理,见失败的任务 |
错误
查询请求出错时返回 4xx / 5xx。这只说明这一次查询没成功,和任务的结果无关:任务该排队还排队、该生成还生成。例如任务不存在:
{
"code": 404,
"message": "Task not found.",
"error": {
"type": "not_found",
"param": null,
"details": null
},
"request_id": "rq_01jb8z9x2c4v6b8n0m1q3w5e7r"
}error.type | 状态码 | 什么时候出现 | 怎么处理 |
|---|---|---|---|
invalid_request | 400 | 任务 ID 超过 64 个字符 | 检查任务 ID |
invalid_api_key | 401 | 没带 Key、格式不对,或者 Key 不存在、已删除、已重置 | 换一个有效的 Key |
account_suspended | 403 | 账号已停用 | 联系 support@nanoapi.ai |
not_found | 404 | 任务不存在,或者不是你的 | 检查任务 ID。任务只能用创建它的账号查询(同账号的任何 Key 都可以) |
rate_limited | 429 | 查询太频繁:所有查询接口(查询任务、列出任务、查询余额、产品列表)加起来每个账号每 10 秒最多 60 次,所有 Key 共享(网站试玩也算在内) | 等 Retry-After 秒后重试。轮询间隔 15 秒以上,多个任务用列出任务的 ids 一次查询,或者改用回调 |
internal_error | 500 | 服务端出错 | 查询没有副作用,可以直接重试 |
任务对象
查询任务的 data、列出任务 data.items 的每一项、回调的 data 都是它(创建任务返回的是精简版)。下面是 data 的内容:
{
"object": "task",
"id": "tk_01jb8z5q4w3e2r1t0y9u8i7o6p",
"model": "genjutsu-1.0",
"status": "succeeded",
"input": {
"mode": "motion_control",
"image_urls": ["https://example.com/person.png"],
"video_url": "https://example.com/dance.mp4",
"prompt": "",
"resolution": "720p"
},
"num_outputs": 1,
"outputs": [
{
"index": 0,
"type": "video",
"url": "https://file.example.com/outputs/tk_01jb8z5q4w3e2r1t0y9u8i7o6p/0.mp4",
"preview_url": "https://file.example.com/outputs/tk_01jb8z5q4w3e2r1t0y9u8i7o6p/0_compressed.mp4"
}
],
"billing": {
"currency": "USD",
"frozen": "4.095",
"charged": "4.095",
"refunded": "0.00",
"billed_seconds": 15
},
"error": null,
"webhook_url": "https://example.com/hooks/nanoapi",
"created_at": "2026-10-22T08:30:00Z",
"started_at": "2026-10-22T08:31:05Z",
"finished_at": "2026-10-22T08:47:12Z",
"elapsed_seconds": 1032
}| 字段 | 说明 |
|---|---|
id | 任务 ID,例如 tk_01jb8z5q4w3e2r1t0y9u8i7o6p,见 ID |
model | 模型版本,例如 genjutsu-1.0 |
status | queued、running、succeeded 或 failed,见状态 |
input | 创建时传入的模型参数 |
num_outputs | 请求的结果个数 |
outputs | 生成结果,任务成功前为空,见结果 |
billing | 金额(美元),见计费字段 |
error | 失败原因 { type, message },未失败时为 null,见失败的任务 |
webhook_url | 创建时传入的回调地址,没有则为 null |
created_at / started_at / finished_at | ISO 8601 时间;started_at 和 finished_at 发生前为 null |
elapsed_seconds | 耗时(秒):从创建到结束,排队和生成都算在内;任务结束前为 null |
状态
queued ──开始生成──→ running ──┬─→ succeeded(扣费)
│ └─→ failed(冻结金额退回)
└──30 分钟内未开始──→ failed(queue_timeout)succeeded 和 failed 是终态,不会再改变。任务不能取消。
结果
| 字段 | 说明 |
|---|---|
index | 序号,从 0 开始 |
type | Genjutsu 为 video |
url | 结果文件,可直接下载 |
preview_url | 宽 600 像素、无声的压缩版,用于预览;没能生成时为 null |
拿到链接的人都能打开文件,不希望公开的结果请不要分享链接。结果文件目前不会删除,但需要长期保存的请复制到你自己的存储中。
计费字段
| 字段 | 说明 |
|---|---|
frozen | 创建任务时从余额中冻结的金额 |
charged | 任务成功时扣除的金额;结束前和失败的任务为 "0.00" |
refunded | 退回可用余额的金额;结束前为 "0.00" |
billed_seconds | 计费秒数(数字,最多 3 位小数),任务成功后才有,否则为 null |
任务结束后:charged + refunded = frozen。见计费。
失败的任务
任务失败的意思是:任务已经创建、费用已经冻结,但没生成出结果。失败的任务:
status为failed。这是终态,不会再变,也不能对它重试。error说明原因:type给程序判断,message是给人看的英文说明。任务失败不是请求出错,所以这里没有 HTTP 状态码。outputs是空数组。- 钱全额退回:
billing.charged为"0.00",billing.refunded等于billing.frozen。冻结的钱已经回到可用余额,你不需要做任何操作。
查询任务的 data、列出任务的 data.items 和回调的 data 里,失败的任务都是这个样子。下面是三个在不同阶段失败的任务,区别在 error、started_at 和结束时间:
开始生成后,发现有一张参考图下载不了(invalid_media)。started_at 有值。
{
"object": "task",
"id": "tk_01jb8z5q4w3e2r1t0y9u8i7o6p",
"model": "genjutsu-1.0",
"status": "failed",
"input": {
"mode": "motion_control",
"image_urls": ["https://example.com/person.png"],
"video_url": "https://example.com/dance.mp4",
"prompt": "",
"resolution": "720p"
},
"num_outputs": 1,
"outputs": [],
"billing": {
"currency": "USD",
"frozen": "4.095",
"charged": "0.00",
"refunded": "4.095",
"billed_seconds": null
},
"error": {
"type": "invalid_media",
"message": "A reference image could not be downloaded. Make sure every link in image_urls is publicly downloadable."
},
"webhook_url": "https://example.com/hooks/nanoapi",
"created_at": "2026-10-22T08:30:00Z",
"started_at": "2026-10-22T08:31:05Z",
"finished_at": "2026-10-22T08:31:40Z",
"elapsed_seconds": 100
}和成功的任务相比,这些字段不同:
| 字段 | 失败的任务 | 说明 |
|---|---|---|
status | "failed" | 终态,不会再变 |
error | { type, message } | type 是失败原因,程序按它判断;message 是英文说明。全部原因和处理方式见失败原因 |
outputs | [] | 没有生成结果 |
billing.charged | "0.00" | 没有扣钱 |
billing.refunded | 等于 billing.frozen | 冻结的钱全额退回可用余额 |
billing.billed_seconds | null | 只有成功的任务才有 |
started_at | 时间或 null | null 表示还没开始生成就失败了:提交时被拒,或排队超时 |
finished_at / elapsed_seconds | 有值 | 失败的时间 / 从创建到失败的秒数 |
失败的任务不能重试。要再生成一次,用同样的参数重新创建任务,会得到新的任务 ID,并重新冻结费用。
失败原因
error.type | 原因 | 怎么处理 | 原样重新提交 |
|---|---|---|---|
content_policy | 内容不符合使用政策:参考图、源视频或提示词含不适宜内容(NSFW),或者含受保护的肖像、版权内容(例如公众人物照片) | 更换素材或提示词 | 还会失败 |
invalid_media | 素材用不了:参考图或源视频下载不了、图片损坏或格式不支持、视频时长不符合要求。message 说明是哪一种 | 按 message 换掉有问题的那份素材 | 还会失败 |
invalid_request | 生成服务不接受这组参数。message 可能指出是哪个参数 | 按 message 调整参数 | 还会失败 |
generation_failed | 生成失败或超时 | 稍后重新创建;连续失败就换素材或提示词 | 可能成功 |
queue_timeout | 排队 30 分钟还没开始生成,一般是高峰期 | 稍后重新创建 | 可能成功 |
internal_error | 我们这边的内部错误 | 稍后重新创建 | 可能成功 |
以后可能新增失败原因,遇到不认识的 type 按普通失败处理,不要报错。
什么时候会失败
| 阶段 | 可能的 error.type | started_at | 在哪里看到 |
|---|---|---|---|
| 提交时就被拒:创建接口把任务交给生成服务,生成服务直接拒收 | content_policy、invalid_media、invalid_request、internal_error | null | 创建接口直接返回 200 和已经 failed 的任务;之后查询、回调里也一样 |
| 排队超时:排队 30 分钟还没开始生成 | queue_timeout | null | 查询、回调 |
| 生成过程中失败 | content_policy、invalid_media、generation_failed、internal_error | 有值 | 查询、回调 |
每种原因的 message
message 是给开发者看的英文说明,适合记日志、排查问题,或者翻译后展示给你的用户。下面是目前会出现的全部 message:
error.type | message | 意思 |
|---|---|---|
content_policy | The input was rejected by the content policy. | 内容没通过审核 |
invalid_media | A reference image could not be downloaded. Make sure every link in image_urls is publicly downloadable. | 有一张参考图下载不了 |
invalid_media | A reference image could not be read. It may be corrupted or in an unsupported format. | 有一张参考图损坏或格式不支持 |
invalid_media | The source video could not be downloaded. Make sure video_url is publicly downloadable. | 源视频下载不了 |
invalid_media | The source video must be 4-30 seconds long. | 源视频时长不符合要求 |
invalid_media | A required input file is missing or could not be used. | 缺少必需的素材 |
invalid_media | An input file could not be used. Check image_urls and video_url. | 其他素材问题 |
invalid_request | The value of <参数名> was rejected. | 某个参数的值被拒,例如 The value of resolution was rejected. |
invalid_request | The task parameters were rejected. | 参数被拒,没能确定是哪个 |
generation_failed | The generation failed. | 生成失败 |
generation_failed | The generation timed out. | 生成超时 |
generation_failed | The task did not finish in time. | 生成迟迟没有结束 |
queue_timeout | The task did not start within 30 minutes. | 排队超时 |
internal_error | An internal error occurred. Please try again later. | 内部错误 |
措辞以后可能调整,也可能增加新的说明,程序只按 type 判断。