直接答案:用 Python 调 Suno API,只需要一个 API Key 和三个步骤:向
/api/music/create提交生成,把返回的song_id放到/api/music/query轮询,状态完成后再调用下载接口取 MP3 或 WAV。不要在生成中反复提交同一请求,否则可能重复计费。
开始前需要准备什么
- 一个在 Suno-API 控制台创建的 API Key。
- Python 3.9 或更高版本,以及
requests。 - 能访问
https://www.suno-api.io的网络。
pip install requests
正确的调用顺序
- 提交生成:
POST /api/music/create。描述、模型和可选参数放在 JSON 请求体里。 - 拿到任务 ID:响应里可能立即返回两条 clip,也可能先返回占位任务;真正用于查询的是最终
song_id。 - 轮询状态:
POST /api/music/query,建议每次间隔 3–5 秒。 - 完成后取地址:调用
/api/music/download-url,再下载 MP3/WAV。 - 及时转存:上游音频地址可能是临时地址;需要长期保存时,下载到自己的对象存储。
可直接运行的 Python 示例
import os
import time
import requests
BASE = "https://www.suno-api.io"
API_KEY = os.environ["SUNO_API_KEY"]
HEADERS = {
"Authorization": f"Bearer {API_KEY}",
"Content-Type": "application/json",
}
def post_json(path, payload):
response = requests.post(
BASE + path,
headers=HEADERS,
json=payload,
timeout=120,
)
if response.status_code == 429:
retry_after = int(response.headers.get("Retry-After", "15"))
time.sleep(retry_after)
return post_json(path, payload)
response.raise_for_status()
return response.json()
def extract_clips(payload):
data = payload.get("data") or {}
return data.get("clips") or payload.get("clips") or []
def generate_and_download(description):
submitted = post_json("/api/music/create", {
"description": description,
"model": "suno-v6",
})
clips = extract_clips(submitted)
if not clips:
raise RuntimeError("生成响应中没有 clips,请检查接口返回或客户端请求 ID")
song_id = clips[0]["song_id"]
deadline = time.time() + 8 * 60
while time.time() < deadline:
result = post_json("/api/music/query", {"song_id": song_id})
data = result.get("data") or {}
status = str(data.get("status", "")).lower()
if status in {"complete", "completed", "playable"}:
break
if status in {"failed", "error"}:
raise RuntimeError(result)
time.sleep(4)
else:
raise TimeoutError("任务等待超过 8 分钟,请稍后用 song_id 继续查询")
media = post_json("/api/music/download-url", {
"song_id": song_id,
"kind": "song",
})
media_data = media.get("data") or {}
media_url = media_data.get("url") or media_data.get("audio_url")
if not media_url:
raise RuntimeError("任务已完成,但下载响应没有媒体地址")
file_response = requests.get(media_url, timeout=180)
file_response.raise_for_status()
with open("output.mp3", "wb") as fh:
fh.write(file_response.content)
return song_id, "output.mp3"
if __name__ == "__main__":
print(generate_and_download("温暖的中文流行,钢琴前奏,女声"))
401、403、429 分别怎么处理
| 状态 | 含义 | 正确处理 |
|---|---|---|
| 401 | Key 错误、缺失或没有带 Bearer | 检查请求头,不要在 URL 参数里传 Key |
| 403 | 余额、账号状态或权限问题 | 先看返回的 error,不要立即重试提交 |
| 429 | 请求频率或轮询过快 | 遵循 Retry-After,并使用指数退避 |
| 5xx | 上游或平台暂时异常 | 保留 song_id 和请求 ID,先查询或联系支持 |
不要重复扣费的三个原则
- 提交接口和查询接口分开;轮询只调查询接口。
- 网络超时后先确认服务端是否已经创建任务,再决定是否重试提交。
- 同一业务请求使用稳定的
client_request_id,方便平台侧做幂等与排查。
常见问题
Python 生成一首歌要等多久?
常见情况是几分钟内完成,但高峰期可能更久。不要用很短的单次请求超时判断失败;提交超时与最终生成超时是两件事。
为什么查询成功却没有音频地址?
任务还在 pending、submitted 或 processing 时,媒体字段为空是正常情况。等状态变成 completed 或 playable 后再获取。
音频地址能长期保存吗?
不要把临时的上游地址当作永久链接。需要长期交付时,完成下载后转存到你自己的对象存储,并在业务数据库中保存新地址。
继续阅读:Node.js 调用示例、任务轮询与超时处理、快速开始、歌曲查询 API。


