---
description: "根据文字描述生成带人声歌曲或纯音乐的异步接口。"
---

# 音乐生成

音乐生成 API 可以根据文字描述创作带人声的完整歌曲或纯音乐。接口采用异步生成方式：先提交一次生成请求并保存返回的 `clip` ID，再通过查询接口获取生成进度和最终音频地址。

## 快速了解

一次完整调用包含以下步骤：

1. 使用公开模型 ID 提交生成请求。
2. 从 HTTP `202` 响应中保存所有 `clips[].id`。
3. 每隔一段时间批量查询这些 ID。
4. 当状态变为 `succeeded` 时，使用 `audio_url` 播放或下载音频。

::: tip
一次生成请求通常会返回多个候选片段。请保存并查询响应中的全部 `clip` ID，不要只处理第一项。
:::

## 接口一览

| 用途 | 方法与路径 | 是否计费 |
|------|------------|----------|
| 提交音乐生成 | `POST /v1/music/generations` | 按所选模型计费一次 |
| 批量查询片段 | `GET /v1/music/clips?ids=...` | 不额外计费 |
| 查询单个片段 | `GET /v1/music/clips/{clip_id}` | 不额外计费 |

所有接口均使用相同的 API Key：

```http
Authorization: Bearer YOUR_API_KEY
```

## 支持的模型

请求中的 `model` 必须填写下列公开模型 ID 之一：

| 模型 ID | 版本 |
|---------|------|
| `suno-v6` | V6（旗舰核心模型） |
| `suno-v6-wild` | V6 Wild（实验性探索） |
| `suno-v6-mini` | V6 Mini（轻量快速） |

不同模型分别按次定价。实际可用模型和当前价格以工作台的模型列表为准。

## 提交音乐生成

> `POST https://oneapi.zaiwenai.com/v1/music/generations`

请求体使用 JSON 格式，并设置 `Content-Type: application/json`。

### 请求参数

| 参数 | 类型 | 必填 | 默认值 | 说明 |
|------|------|------|--------|------|
| `model` | string | 是 | - | 使用上方列出的公开模型 ID |
| `mode` | string | 否 | `simple` | `simple` 或 `custom` |
| `prompt` | string | 视模式而定 | `""` | 简单模式中的歌曲描述，或自定义模式中的歌词 |
| `instrumental` | boolean | 否 | `false` | `true` 生成纯音乐，`false` 生成带人声歌曲 |
| `style` | string | 自定义模式必填 | - | 曲风、乐器、速度、情绪等风格说明 |
| `title` | string | 自定义模式必填 | - | 歌曲标题 |
| `negative_style` | string | 否 | - | 希望避免的风格或声音元素 |
| `vocal_gender` | string | 否 | - | 人声倾向，可选 `male` 或 `female` |
| `style_weight` | number | 否 | - | 风格约束强度，范围为 `0` 到 `1` |
| `weirdness` | number | 否 | - | 创意和意外程度，范围为 `0` 到 `1` |

不支持的字段会被拒绝。建议仅发送当前场景实际需要的参数。

### 简单模式

简单模式适合快速创作。你只需要用自然语言描述想要的歌曲，系统会自动组织标题、歌词、风格和编曲。

简单模式规则：

- `mode` 省略时默认为 `simple`。
- `prompt` 必填且不能为空。
- `instrumental=false` 时生成带人声歌曲。
- `instrumental=true` 时生成纯音乐。

#### 生成带人声歌曲

```bash
curl https://oneapi.zaiwenai.com/v1/music/generations \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "suno-v6",
    "mode": "simple",
    "prompt": "一首温暖的中文流行歌曲，主题是雨后重新出发，女声演唱，副歌明亮而有记忆点",
    "instrumental": false
  }'
```

#### 生成纯音乐

```bash
curl https://oneapi.zaiwenai.com/v1/music/generations \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "suno-v6-mini",
    "prompt": "轻快的电子氛围音乐，清晰的鼓点和明亮的合成器，适合作为科技产品演示背景音乐",
    "instrumental": true
  }'
```

### 自定义模式

自定义模式适合需要明确控制标题、歌词和曲风的场景。

自定义模式规则：

- `mode` 必须设置为 `custom`。
- `style` 和 `title` 必填且不能为空。
- 生成带人声歌曲时，`prompt` 必填，并作为歌词使用。
- 生成纯音乐时，将 `instrumental` 设为 `true`，此时 `prompt` 可以留空。

#### 使用自定义歌词

```bash
curl https://oneapi.zaiwenai.com/v1/music/generations \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "suno-v6",
    "mode": "custom",
    "title": "雨后的站台",
    "style": "中文流行，温暖女声，中速，钢琴与弦乐，副歌逐渐增强",
    "prompt": "[Verse]\n雨停在清晨的站台\n风把昨天轻轻吹开\n\n[Chorus]\n我向着亮起来的天\n把新的故事写一遍",
    "instrumental": false,
    "vocal_gender": "female",
    "style_weight": 0.7,
    "weirdness": 0.25
  }'
```

#### 自定义纯音乐

```bash
curl https://oneapi.zaiwenai.com/v1/music/generations \
  -H "Authorization: Bearer $YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "model": "suno-v6-wild",
    "mode": "custom",
    "title": "Neon Morning",
    "style": "synthwave, cinematic, mid-tempo, bright arpeggios, punchy drums",
    "prompt": "",
    "instrumental": true,
    "negative_style": "distorted vocals, harsh noise",
    "style_weight": 0.8,
    "weirdness": 0.2
  }'
```

### 如何编写提示词

简单模式的 `prompt` 建议包含以下信息：

- **音乐类型**：流行、摇滚、电子、民谣、爵士、古典等。
- **情绪氛围**：温暖、忧郁、振奋、梦幻、紧张、平静等。
- **节奏速度**：慢速、中速、快节奏，或者描述鼓点特征。
- **主要乐器**：钢琴、吉他、弦乐、合成器、鼓组等。
- **人声要求**：语言、男声或女声、演唱质感。
- **使用场景**：短视频背景、游戏场景、播客片头、品牌展示等。

比起堆叠大量互相冲突的形容词，清晰表达音乐类型、情绪和核心乐器通常更容易得到稳定结果。

自定义歌词可以使用 `[Verse]`、`[Chorus]`、`[Bridge]` 等段落标记帮助表达歌曲结构。段落标记属于歌词内容的一部分，不需要额外字段。

### 提交成功响应

请求被接受后返回 HTTP `202`：

```json
{
  "object": "music.generation",
  "model": "suno-v6",
  "clips": [
    {
      "id": "87d7be70-6fb7-494b-a465-436332eadf02",
      "object": "music.clip",
      "model": "chirp-hawk",
      "status": "pending"
    },
    {
      "id": "7218e443-c702-46c1-903a-183f48db5f73",
      "object": "music.clip",
      "model": "chirp-hawk",
      "status": "pending"
    }
  ]
}
```

| 字段 | 说明 |
|------|------|
| `object` | 固定为 `music.generation` |
| `model` | 本次请求使用的公开模型 ID |
| `clips` | 本次生成产生的候选片段列表 |
| `clips[].id` | 后续查询使用的片段 ID，必须保存 |
| `clips[].status` | 当前片段状态，提交时通常为 `pending` 或 `partial` |

`clips[].model` 是结果记录携带的模型标识，具体值以实际响应为准。选择模型、统计费用或重新发起请求时，应使用提交请求时的公开模型 ID，而不是依赖该字段。

## 查询生成结果

### 批量查询

推荐优先使用批量查询，一次可以查询最多 20 个片段 ID。

> `GET https://oneapi.zaiwenai.com/v1/music/clips?ids={id1},{id2}`

```bash
curl "https://oneapi.zaiwenai.com/v1/music/clips?ids=87d7be70-6fb7-494b-a465-436332eadf02,7218e443-c702-46c1-903a-183f48db5f73" \
  -H "Authorization: Bearer $YOUR_API_KEY"
```

响应格式：

```json
{
  "object": "list",
  "data": [
    {
      "id": "87d7be70-6fb7-494b-a465-436332eadf02",
      "object": "music.clip",
      "model": "chirp-hawk",
      "status": "succeeded",
      "title": "雨后的站台",
      "lyrics": "雨停在清晨的站台……",
      "style": "中文流行，钢琴，弦乐",
      "audio_url": "https://example.com/generated-song.mp3",
      "video_url": "https://example.com/generated-song.mp4",
      "image_url": "https://example.com/generated-cover.jpeg",
      "duration": 188.84,
      "created_at": "2026-08-04T07:50:59.078Z"
    }
  ]
}
```

### 查询单个片段

> `GET https://oneapi.zaiwenai.com/v1/music/clips/{clip_id}`

```bash
curl https://oneapi.zaiwenai.com/v1/music/clips/87d7be70-6fb7-494b-a465-436332eadf02 \
  -H "Authorization: Bearer $YOUR_API_KEY"
```

成功时直接返回一个 `music.clip` 对象，不再包含外层的 `data` 数组。

### 片段字段

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | string | 片段唯一 ID |
| `object` | string | 固定为 `music.clip` |
| `model` | string | 结果记录携带的模型标识 |
| `status` | string | 当前生成状态 |
| `title` | string | 歌曲标题，尚未生成时可能为空 |
| `lyrics` | string | 歌词；纯音乐或处理中可能为空 |
| `style` | string | 结果使用的风格描述 |
| `audio_url` | string | 最终音频地址；成功前可能为空 |
| `video_url` | string | 音乐视频地址；没有视频时可能为空 |
| `image_url` | string | 封面图片地址；生成完成前可能为空 |
| `duration` | number | 音频时长，单位为秒 |
| `created_at` | string 或 integer | 创建时间 |
| `error` | object | 失败原因，正常情况下为空或不返回 |

响应会省略当前没有值的可选字段。客户端应按字段是否存在来读取，不要假定所有字段始终返回。

### 状态说明

| 状态 | 是否终态 | 说明 |
|------|----------|------|
| `pending` | 否 | 已进入队列，等待开始生成 |
| `partial` | 否 | 已产生部分结果，仍需继续查询 |
| `running` | 否 | 正在生成或处理 |
| `succeeded` | 是 | 已完成；确认 `audio_url` 后即可使用 |
| `failed` | 是 | 生成失败；查看 `error.message` |

建议每隔 `10` 至 `20` 秒查询一次。到达 `succeeded` 或 `failed` 后停止轮询。不要以固定等待时长代替状态判断，因为不同歌曲的生成时间可能不同。

::: warning
收到 `partial` 时不要将任务视为完成。即使响应中已经出现部分可用字段，也应继续查询到终态。
:::

## 完整 Python 示例

下面的示例会提交请求、批量轮询全部片段，并在完成后打印音频地址：

```python
import time

import requests

BASE_URL = "https://oneapi.zaiwenai.com/v1"
API_KEY = "YOUR_API_KEY"
HEADERS = {
    "Authorization": f"Bearer {API_KEY}",
    "Content-Type": "application/json",
}

generation_response = requests.post(
    f"{BASE_URL}/music/generations",
    headers=HEADERS,
    json={
        "model": "suno-v6",
        "mode": "simple",
        "prompt": "温暖的中文流行歌曲，女声，中速，钢琴与弦乐",
        "instrumental": False,
    },
    timeout=180,
)
generation_response.raise_for_status()
generation = generation_response.json()

clip_ids = [clip["id"] for clip in generation["clips"]]
print("已提交：", clip_ids)

while True:
    query_response = requests.get(
        f"{BASE_URL}/music/clips",
        headers={"Authorization": f"Bearer {API_KEY}"},
        params={"ids": ",".join(clip_ids)},
        timeout=30,
    )
    query_response.raise_for_status()
    clips = query_response.json()["data"]

    print({clip["id"]: clip["status"] for clip in clips})
    if all(clip["status"] in {"succeeded", "failed"} for clip in clips):
        break

    time.sleep(15)

for clip in clips:
    if clip["status"] == "succeeded":
        print(clip.get("title"), clip.get("audio_url"))
    else:
        print(clip["id"], "生成失败：", clip.get("error"))
```

## 完整 JavaScript 示例

以下示例适用于支持原生 `fetch` 的 Node.js 环境：

```javascript
const baseUrl = "https://oneapi.zaiwenai.com/v1";
const apiKey = "YOUR_API_KEY";
const headers = {
  Authorization: `Bearer ${apiKey}`,
  "Content-Type": "application/json",
};

const generationResponse = await fetch(`${baseUrl}/music/generations`, {
  method: "POST",
  headers,
  body: JSON.stringify({
    model: "suno-v6",
    mode: "simple",
    prompt: "温暖的中文流行歌曲，女声，中速，钢琴与弦乐",
    instrumental: false,
  }),
});

if (!generationResponse.ok) {
  throw new Error(await generationResponse.text());
}

const generation = await generationResponse.json();
const clipIds = generation.clips.map((clip) => clip.id);
let clips = [];

while (true) {
  await new Promise((resolve) => setTimeout(resolve, 15000));

  const ids = encodeURIComponent(clipIds.join(","));
  const queryResponse = await fetch(`${baseUrl}/music/clips?ids=${ids}`, {
    headers: { Authorization: `Bearer ${apiKey}` },
  });

  if (!queryResponse.ok) {
    throw new Error(await queryResponse.text());
  }

  clips = (await queryResponse.json()).data;
  if (clips.every((clip) => ["succeeded", "failed"].includes(clip.status))) {
    break;
  }
}

for (const clip of clips) {
  console.log(clip.status, clip.title, clip.audio_url, clip.error);
}
```

不要把 API Key 放入公开网页或浏览器前端代码。生产环境应由你自己的服务端安全保存并使用 API Key。

## 计费说明

音乐生成按照所选模型的**请求次数**计费：

- 一次被接受的 `POST /v1/music/generations` 按所选模型计费一次。
- 同一次请求返回多个候选片段时，仍按这一次请求计费。
- 查询单个或批量片段不会产生额外音乐生成费用。
- 对同一提示词重复提交 `POST` 会被视为新的生成请求，并再次计费。
- 任务后续状态变化不会产生第二次费用。

提交前会检查账户余额。余额不足时返回 HTTP `402`，不会接受生成请求。具体单价和消费记录可在工作台中查看。

::: warning 避免重复提交
如果客户端因网络问题没有收到提交响应，不要立即无限重试。先检查调用记录，并为自动重试设置明确的次数上限，避免创建和计费多次生成请求。
:::

## 常见错误

错误响应使用统一结构：

```json
{
  "error": {
    "message": "错误说明",
    "type": "错误类型",
    "param": "相关参数",
    "code": "错误代码"
  }
}
```

| HTTP 状态码 | 常见原因 | 处理建议 |
|-------------|----------|----------|
| `400` | 模型不支持或模式参数组合无效 | 检查模型 ID、`mode` 和必填字段 |
| `401` | API Key 缺失或无效 | 检查 `Authorization` 请求头 |
| `402` | 账户余额不足 | 充值后重新提交 |
| `404` | 查询的片段不存在 | 检查 `clip_id` 是否完整、正确 |
| `422` | 字段类型、取值范围或请求格式错误 | 根据响应中的字段提示修正 JSON |
| `502` | 本次生成或查询结果暂时不可用 | 稍后重试查询；提交失败时谨慎重试 |
| `503` | 服务暂时不可用或连接超时 | 稍后重试，并使用退避策略限制频率 |

## 接入建议

- 将每次提交返回的全部 `clip` ID 与你自己的业务记录关联。
- 使用批量查询减少请求次数，并把轮询间隔设置为 `10` 至 `20` 秒。
- 以 `status` 判断任务是否完成，不要以 `audio_url` 是否出现作为唯一依据。
- 对 `failed` 状态记录 `error.message`，不要继续轮询失败片段。
- 播放或下载前确认 `audio_url` 非空。
- 用户主动取消页面等待时，仍可保留片段 ID，稍后继续查询。
- 设置合理的提交超时和查询超时，但不要因一次客户端超时无限重复提交。

## 常见问题

### 为什么提交接口没有直接返回完整歌曲？

歌曲生成需要一定时间，因此提交接口先返回可查询的片段 ID。客户端可以继续处理其他工作，并通过查询接口获取进度。

### 一次请求为什么返回多个片段？

一次创作可能提供多个候选结果。每个候选片段都有独立 ID、状态和音频地址，应分别处理。

### 可以只查询其中一个片段吗？

可以。使用单个片段查询接口即可；需要同时跟踪多个候选结果时，推荐使用批量查询。

### 查询状态会重复扣费吗？

不会。音乐生成只在提交请求被接受时按次计费，后续状态查询不额外收费。

### 可以直接使用返回的音频地址吗？

可以。任务成功并返回非空 `audio_url` 后，可以用该地址播放或下载音频。是否需要另外保存副本，可根据你的业务归档需求决定，调用接口本身不要求配置对象存储。
