> Markdown copy of https://makernice.com/zh-CN/docs/python/ for AI tools. The HTML page is the canonical version.

# 用 Python 调用 Makernice API

用 Python 的 `requests` 库调用 Makernice：先报精确价格，再创建生成、轮询到完成，最后下载文件。一共四次调用，不需要安装 SDK。

[Makernice 团队](https://makernice.com/zh-CN/about/) · 更新于 2026 年 11 月 11 日

## 开始之前

-   在[控制台](https://makernice.com/console/keys/)创建 API Key，设成环境变量 `MAKERNICE_API_KEY`。
-   Python 3，并执行 `pip install requests`。

## 完整示例

quickstart.py

```
import os
import time
import uuid
from decimal import Decimal

import requests

API = "https://api.makernice.com/v1"
ORIGIN = "https://api.makernice.com"
HEADERS = {"Authorization": f"Bearer {os.environ['MAKERNICE_API_KEY']}"}
TIMEOUT_SECONDS = 30
POLL_INTERVAL_SECONDS = 5
MAX_WAIT_SECONDS = 30 * 60
FINAL = {"succeeded", "failed", "canceled"}

def call(method, path, headers=None, **kwargs):
    # Waits out 429 (rate_limited, concurrency_limit_reached) using Retry-After; raises on any other error.
    for _ in range(5):
        r = requests.request(method, API + path, headers={**HEADERS, **(headers or {})}, timeout=TIMEOUT_SECONDS, **kwargs)
        if r.status_code == 429:
            time.sleep(int(r.headers.get("Retry-After", "5")))
            continue
        if not r.ok:
            raise RuntimeError(f"{method} {path} -> {r.status_code}: {r.text}")
        return r.json()
    raise RuntimeError(f"{method} {path}: still rate limited")

job = {
    "model": "seedance-2.5",
    "mode": "t2v",
    "prompt": "A slow dolly shot through a greenhouse",
    "duration": 5,
    "resolution": "720p",
}

# 1. Quote: the exact amount, locked for 12 minutes
quote = call("POST", "/generations/quote", json=job)
usd = Decimal(quote["amount"]).scaleb(-6)  # micro-USD integer string -> dollars, no float rounding
print(f"Quoted ${usd} (price list v{quote['price_version']}), valid until {quote['expires_at']}")

# 2. Create: same input + quote_id. A retry with the same Idempotency-Key returns the same generation
idempotency_key = str(uuid.uuid4())
gen = call("POST", "/generations", json={"quote_id": quote["id"], "input": job}, headers={"Idempotency-Key": idempotency_key})

# 3. Poll: queued -> generating -> moderating -> succeeded | failed | canceled (checking: keep polling)
deadline = time.monotonic() + MAX_WAIT_SECONDS
while gen["status"] not in FINAL:
    if time.monotonic() > deadline:
        raise SystemExit(f"{gen['id']} is still {gen['status']}; poll it again later")
    time.sleep(POLL_INTERVAL_SECONDS)
    gen = call("GET", f"/generations/{gen['id']}")

if gen["status"] != "succeeded":
    # billing is "released": the hold went back to your balance
    raise SystemExit(f"{gen['status']}, billing {gen['billing']}: {gen['error']}")

# 4. Download: outputs[].url is relative to the API origin and redirects (302) to the file
output = gen["outputs"][0]
if output["state"] == "blocked":
    raise SystemExit("Output blocked by moderation (the run is charged); contact support for a manual review")
r = requests.get(ORIGIN + output["url"], headers=HEADERS, timeout=120)  # requests drops Authorization on the redirect
r.raise_for_status()
with open("output.mp4", "wb") as f:
    f.write(r.content)
print(f"Saved output.mp4, charged ${Decimal(gen['amount']).scaleb(-6)}")
```

## 代码做了什么

1.  **报价**：`POST /v1/generations/quote` 返回精确金额（micro-USD 整数字符串），锁定 12 分钟，这一步不扣费。
2.  **创建**：`POST /v1/generations` 带上 `quote_id`、同一份输入和 `Idempotency-Key` 请求头。用同一个键重试，拿回的是同一个生成，不会重复下单。
3.  **等待**：轮询 `GET /v1/generations/{id}` 直到 `succeeded`、`failed` 或 `canceled`；或者传 `callback_url`，完成后我们把结果 POST 到你的服务器。
4.  **下载**：每个 `outputs[].url` 返回 302，跳到带签名的文件地址。

## Python 要注意的地方

-   金额是 micro-USD 整数字符串，示例用 `Decimal` 换算，不用 `float`，不会有舍入误差。
-   429 响应带 `Retry-After`，`call()` 会按它等待，最多重试 5 次。
-   `requests` 跟随跨主机的下载跳转时会去掉 `Authorization` 请求头，这正是想要的：签名文件地址不需要你的 Key。

## 常见问题

### 有官方 SDK 吗？

没有。REST API 只有四次调用，本页代码就是完整的客户端。想自动生成客户端，可以用 [/openapi.json](https://makernice.com/openapi.json) 的 OpenAPI 描述。

### 该轮询还是用 webhook？

脚本和批量任务用轮询就够了。Web 应用建议传 `callback_url` 并验证签名：[FastAPI 接入](https://makernice.com/zh-CN/docs/fastapi/)里有完整的接收端，同一个 `verify_webhook` 函数在 Django（`request.body`）和 Flask（`request.get_data()`）里也能直接用。

### 生成失败要付钱吗？

不用。创建时冻结金额，失败或取消就全额释放；生成完成后被内容审核拦截的仍会扣费。详见[失败不扣费](https://makernice.com/zh-CN/pricing/failed-generations/)。

## 相关链接

-   [API 文档：快速上手](https://makernice.com/zh-CN/docs/#quickstart)
-   [API 文档：Webhooks](https://makernice.com/zh-CN/docs/#webhooks)
-   [API 文档：错误码](https://makernice.com/zh-CN/docs/#errors)
-   [用 Node.js 调用 Makernice API](https://makernice.com/zh-CN/docs/node/)
-   [用 Next.js 接入 Makernice API](https://makernice.com/zh-CN/docs/nextjs/)
-   [用 FastAPI 接入 Makernice API](https://makernice.com/zh-CN/docs/fastapi/)
-   [在 Claude Code、Cursor 等 AI 工具里用（MCP）](https://makernice.com/zh-CN/mcp/)
