> ## Documentation Index
> Fetch the complete documentation index at: https://docs.apimart.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# 下载音频文件

>  - 将 Suno 歌曲下载为 MP3、M4A 或 WAV 文件
- 支持一次请求多个格式，返回对应的文件 URL
- 使用源任务 task_id 和 audio_index 指定要下载的歌曲
- 异步提交任务，通过音乐任务查询接口获取结果 

> [!WARNING]
>
> 原 `POST /v1/music/generations/wav` 接口已弃用。旧接口暂时兼容，等价于调用新接口并传入 `formats: ["wav"]`；新代码请统一使用 `POST /v1/music/generations/download`。

> [!NOTE]
>
> **指定源歌曲：** 传入生成源音轨时获得的 `task_id`，并用 `audio_index` 指定结果 `data.result.music[]` 中的第几首歌曲。`audio_index` 从 1 开始，默认值为 1。

```bash cURL theme={null}
curl --request POST \
  --url https://api.apimart.ai/v1/music/generations/download \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '{
    "model": "suno",
    "task_id": "task_01JGXXXXXXXXXXXX",
    "audio_index": 1,
    "formats": ["mp3", "wav"]
  }'
```

```python Python theme={null}
import requests

url = "https://api.apimart.ai/v1/music/generations/download"

payload = {
    "model": "suno",
    "task_id": "task_01JGXXXXXXXXXXXX",
    "audio_index": 1,
    "formats": ["mp3", "wav"],
}

headers = {
    "Authorization": "Bearer <token>",
    "Content-Type": "application/json",
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

```javascript JavaScript theme={null}
const response = await fetch(
  "https://api.apimart.ai/v1/music/generations/download",
  {
    method: "POST",
    headers: {
      Authorization: "Bearer <token>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      model: "suno",
      task_id: "task_01JGXXXXXXXXXXXX",
      audio_index: 1,
      formats: ["mp3", "wav"],
    }),
  },
);

console.log(await response.json());
```

```go Go theme={null}
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "net/http"
)

func main() {
    payload := map[string]interface{}{
        "model":       "suno",
        "task_id":     "task_01JGXXXXXXXXXXXX",
        "audio_index": 1,
        "formats":     []string{"mp3", "wav"},
    }

    body, _ := json.Marshal(payload)
    req, _ := http.NewRequest(
        http.MethodPost,
        "https://api.apimart.ai/v1/music/generations/download",
        bytes.NewBuffer(body),
    )
    req.Header.Set("Authorization", "Bearer <token>")
    req.Header.Set("Content-Type", "application/json")

    response, _ := http.DefaultClient.Do(req)
    defer response.Body.Close()
    result, _ := io.ReadAll(response.Body)
    fmt.Println(string(result))
}
```

```java Java theme={null}
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Main {
    public static void main(String[] args) throws Exception {
        String payload = """
        {
          "model": "suno",
          "task_id": "task_01JGXXXXXXXXXXXX",
          "audio_index": 1,
          "formats": ["mp3", "wav"]
        }
        """;

        HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("https://api.apimart.ai/v1/music/generations/download"))
            .header("Authorization", "Bearer <token>")
            .header("Content-Type", "application/json")
            .POST(HttpRequest.BodyPublishers.ofString(payload))
            .build();

        HttpResponse<String> response = HttpClient.newHttpClient().send(
            request,
            HttpResponse.BodyHandlers.ofString()
        );
        System.out.println(response.body());
    }
}
```

```php PHP theme={null}
<?php

$url = "https://api.apimart.ai/v1/music/generations/download";
$payload = [
    "model" => "suno",
    "task_id" => "task_01JGXXXXXXXXXXXX",
    "audio_index" => 1,
    "formats" => ["mp3", "wav"]
];

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
    "Authorization: Bearer <token>",
    "Content-Type: application/json"
]);

echo curl_exec($ch);
curl_close($ch);
?>
```

```ruby Ruby theme={null}
require "net/http"
require "json"

uri = URI("https://api.apimart.ai/v1/music/generations/download")
request = Net::HTTP::Post.new(uri)
request["Authorization"] = "Bearer <token>"
request["Content-Type"] = "application/json"
request.body = {
  model: "suno",
  task_id: "task_01JGXXXXXXXXXXXX",
  audio_index: 1,
  formats: ["mp3", "wav"]
}.to_json

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts response.body
```

```swift Swift theme={null}
import Foundation

let url = URL(string: "https://api.apimart.ai/v1/music/generations/download")!
let payload: [String: Any] = [
    "model": "suno",
    "task_id": "task_01JGXXXXXXXXXXXX",
    "audio_index": 1,
    "formats": ["mp3", "wav"]
]

var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer <token>", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = try? JSONSerialization.data(withJSONObject: payload)

URLSession.shared.dataTask(with: request) { data, _, error in
    if let error = error {
        print(error)
        return
    }
    if let data = data {
        print(String(data: data, encoding: .utf8) ?? "")
    }
}.resume()
```

```json 200 theme={null}
{
  "code": 200,
  "data": [
    {
      "status": "submitted",
      "task_id": "task_01JHXXXXXXXXXXXX"
    }
  ]
}
```

```json 400 theme={null}
{
  "error": {
    "message": "`formats` contains unsupported format `flac`. Supported: mp3 / m4a / wav",
    "type": "invalid_request_error",
    "code": "invalid_source_reference"
  }
}
```

```json 401 theme={null}
{
  "error": {
    "code": 401,
    "message": "身份验证失败，请检查您的 API 密钥",
    "type": "authentication_error"
  }
}
```

```json 402 theme={null}
{
  "error": {
    "code": 402,
    "message": "账户余额不足，请充值后再试",
    "type": "payment_required"
  }
}
```

```json 403 theme={null}
{
  "error": {
    "code": "model_price_not_configured",
    "message": "模型价格未配置",
    "type": "permission_error"
  }
}
```

```json 429 theme={null}
{
  "error": {
    "code": 429,
    "message": "请求过于频繁，请稍后再试",
    "type": "rate_limit_error"
  }
}
```

```json 500 theme={null}
{
  "error": {
    "code": 500,
    "message": "服务器内部错误，请稍后重试",
    "type": "server_error"
  }
}
```

## 认证

**Authorization** `string` *required*

所有接口均使用 Bearer Token 认证。访问 [API Key 管理页面](https://apimart.ai/keys) 获取 API Key。

```
Authorization: Bearer YOUR_API_KEY
```

## 请求参数

**model** `string` (default: suno)

模型名称。当前填写 `suno`；省略时默认为 `suno`。

**task\_id** `string` *required*

生成源歌曲时返回的任务 ID。

源任务必须：

* 属于当前账户
* 已完成
* 包含可下载的音轨

音乐生成、延长、翻唱、分轨等音频任务均可作为来源；歌词、BPM 等仅返回文本的任务无法下载。

**audio\_index** `integer` (default: 1)

要下载源任务结果 `data.result.music[]` 中的第几首歌曲。

* 从 `1` 开始计数
* 默认：`1`
* 不得超过源任务实际包含的歌曲数量

**formats** `string[]`

要下载的文件格式数组，至少包含一项。

可选值：

* `mp3`
* `m4a`
* `wav`

支持同时请求多个格式；格式名大小写不敏感，重复值会自动去重。结果顺序与请求顺序一致。

**format** `string`

只下载一种格式时，可使用此字段代替 `formats`。

示例：`"format": "mp3"`

> [!NOTE]
>
> `formats` 和 `format` 选择一种写法即可。两者均未提供或格式列表为空时，请求会返回 HTTP 400。

## 提交响应

成功提交后返回下载任务自己的 `task_id`：

```json theme={null}
{
  "code": 200,
  "data": [
    {
      "status": "submitted",
      "task_id": "task_01JHXXXXXXXXXXXX"
    }
  ]
}
```

> [!WARNING]
>
> 返回的 `data` 是数组，请读取 `data[0].task_id`。它是新建的下载任务 ID，与请求中的源歌曲 `task_id` 不同。

## 查询下载结果

使用提交响应中的下载任务 ID 查询：

```http theme={null}
GET /v1/music/tasks/{task_id}
```

文件通常已在提交阶段准备完成，因此提交后可以立即查询一次。如果状态不是 `completed` 或 `failed`，再每 2 秒轮询一次，最多等待 60 秒。

### 下载完成

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_01JHXXXXXXXXXXXX",
    "status": "completed",
    "progress": 100,
    "created": 1756800000,
    "completed": 1756800003,
    "actual_time": 3,
    "cost": 0.01,
    "credits_cost": 0.1,
    "result": {
      "music_id": "518c74ee-62ac-4ccd-b3d9-7003acd12ad7",
      "files": [
        {
          "format": "mp3",
          "url": "https://assets.apimart.ai/audio/example.mp3"
        },
        {
          "format": "wav",
          "url": "https://assets.apimart.ai/audio/example.wav"
        }
      ],
      "wavUrl": "https://assets.apimart.ai/audio/example.wav"
    }
  }
}
```

读取 `result.files[]` 获取下载文件：

| 字段 | 类型 | 说明 |
| - | - | - |
| `format` | string | 文件格式：`mp3` / `m4a` / `wav` |
| `url` | string | 文件下载地址 |

> [!NOTE]
>
> `result.wavUrl` 仅用于兼容旧 WAV 接口。新代码请统一读取 `result.files[]`。

### 处理中

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_01JHXXXXXXXXXXXX",
    "status": "processing",
    "progress": 50,
    "created": 1756800000
  }
}
```

此时没有 `result` 字段，请继续轮询。

### 下载失败

```json theme={null}
{
  "code": 200,
  "data": {
    "id": "task_01JHXXXXXXXXXXXX",
    "status": "failed",
    "progress": 100,
    "cost": 0,
    "error": {
      "message": "文件处理失败"
    }
  }
}
```

失败任务会自动退款，`cost` 为 `0`。可向用户显示 `error.message` 并提供重试操作。

## 文件 URL

下载结果通常使用 APIMart 文件域名。返回的文件 URL 可能存在有效期，请在任务完成后及时下载并保存。

> [!WARNING]
>
> 请在获得 URL 后尽快下载并保存文件，不要将临时 URL 作为长期存储地址。

## 错误处理

提交期参数错误会返回 HTTP 400，不创建任务、不扣费：

| 错误信息 | 原因 |
| - | - |
| `formats is required` / `must contain at least one` | 未提供下载格式 |
| `unsupported format` | 包含 `mp3` / `m4a` / `wav` 以外的格式 |
| `task_id is required` / `invalid task_id format` | 源任务 ID 缺失或格式错误 |
| `source task not found` | 源任务不存在或不属于当前账户 |
| `audio_index N out of range` | 歌曲序号超出源任务音轨数量 |
| `track #N has no music_id` | 源任务未完成或对应歌曲没有音频 |

HTTP 403 且错误码为 `model_price_not_configured` 时，表示后台未配置 `suno@download` 价格，请联系平台支持。

## 计费与重复下载

下载接口按请求计费：

* 一次请求选择多个格式只收取一次费用
* 再次提交同一首歌曲，即使格式相同，也会产生新费用
* 下载另一种格式需要新建任务，也会产生新费用
* 下载失败会自动退款

> [!WARNING]
>
> 请保存已返回的文件 URL，并在请求处理中禁用下载按钮，避免重复提交和重复扣费。

## 从旧接口迁移

| 项目 | 旧接口 | 新接口 |
| - | - | - |
| 请求路径 | `/v1/music/generations/wav` | `/v1/music/generations/download` |
| 格式 | 仅 WAV | MP3 / M4A / WAV，可多选 |
| 新增参数 | — | `formats` 或 `format` |
| 结果字段 | `result.wavUrl` | `result.files[]` |
| 兼容字段 | `result.wavUrl` | 请求 WAV 时仍可能返回 `result.wavUrl` |

旧接口仍可暂时使用，但新功能和新代码应切换到 `/generations/download`。

## Response

**code** `integer`

响应状态码，成功时为 200

**data** `array`

提交响应数据。

**数组元素**

**status** `string`

初始状态为 `submitted`

**task\_id** `string`

下载任务 ID，用于轮询 `GET /v1/music/tasks/{task_id}`


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.