Upload-then-cover API
Endpoint description
Upload-then-cover submits an audio file or an audio URL and generates a new cover version from that source audio.
Endpoint: POST /api/music/upload-cover
Billing: Paid, currently 0.6 ¥ per call by default. This endpoint is "upload then cover" — it is not about uploading an image cover. The amount actually charged follows the current back-office price configuration.
Max Mode / Variety: this endpoint supports the optional parameters max_mode and variety (0-4). With
max_mode on, this call is billed at 2× the price (0.6 → 1.2 ¥ per call); variety costs nothing extra. What
they mean and how to send them is in the "Advanced parameters" section of the
New API overview.
Wait times and timeouts (important): this is a synchronous endpoint — the order is only accepted and handed to generation when the response arrives. Measured, most requests return in 1-3 seconds, about 10% take longer than 30 seconds, and the longest is about 230 seconds. Set your client timeout to 300 seconds or more; no response does not mean nothing was generated, and do not retry immediately after a timeout (that duplicates the order and charges again). If the connection dropped, confirm later with
GET /api/music/songs. See "Wait times and timeout settings" in the New API overview.
When the audio is large, split "upload" and "cover" into two steps (recommended)
This endpoint requires the whole audio to go into the request body: the bigger the audio, the bigger the body, and one network hiccup or timeout cuts the transfer halfway, after which the server returns
unexpected end of JSON input(HTTP 400, no charge).When the audio is over about 8 MB, the network is unstable, or the same source material will be reused, switch to the two-step form:
POST /api/music/upload-sourceuploads the source audio first — free, retry anytime — and gives you aclip_id;POST /api/music/generate-from-sourcestarts the cover with thatclip_id— the body of the charged step is only about 250 bytes.See the upload source audio API, the cover from a source clip API, and "Large files and truncated requests" at the end of this page.
Request parameters
Headers
| Parameter | Type | Required | Notes |
|---|---|---|---|
| Authorization | string | yes | Bearer YOUR_API_KEY |
| Content-Type | string | yes | application/json |
Body parameters
| Parameter | Type | Required | Default | Notes |
|---|---|---|---|---|
| model | string | no | suno-v6 | Customer-facing model name: suno-v6, suno-v6-wild or suno-v6-mini |
| file_url | string | no | - | Source audio URL |
| file_base64 | string | no | - | Base64 audio |
| file_name | string | no | - | File name |
| prompt | string | no | - | New lyrics or the direction of the adaptation |
| style | string | no | - | Style tags |
| title | string | no | - | Title |
| negativeTags | string | no | - | Negative style |
| instrumental | boolean | no | false | Whether the song is instrumental |
| waitAudio | boolean | no | false | Whether to wait for the audio to complete |
| cover_start_s | number | no | - | Start second of the covered segment |
| cover_end_s | number | no | - | End second of the covered segment |
| audio_weight | number | no | - | Influence weight of the reference audio, range 0.0-1.0; can only be sent when reference audio is provided |
cover_start_s and cover_end_s are in seconds and may be decimals; the start time must be smaller than the end time and must not exceed the source audio duration. When both fields are omitted, the whole audio is used. audio_weight maps to Suno's metadata.control_sliders.audio_weight and controls how far the result leans on the reference audio; it takes 0.0-1.0 and may only be carried when the request provides a valid file_url or file_base64 reference audio. 0 is a valid value; omitting the field means "not specified", which keeps Suno's default behaviour. instrumental and waitAudio must be JSON booleans, not strings.
The upload endpoint returns asynchronously by default. While clips[].status in the response is pending, an empty audio_url is normal and does not mean the upload or the cover failed; save the real song_id you get back and poll the song query endpoint until it completes before reading the audio address.
Request example
curl -X POST https://www.suno-api.io/api/music/upload-cover \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-v6",
"file_url": "https://example.com/source.mp3",
"file_name": "source.mp3",
"prompt": "New lyrics or direction of the adaptation",
"style": "Mandopop, acoustic guitar",
"title": "Cover version",
"negativeTags": "noise, low quality",
"audio_weight": 0.5,
"instrumental": false,
"waitAudio": false,
"cover_start_s": 0,
"cover_end_s": 60
}'
Response
{
"code": 200,
"message": "success",
"data": {
"upload": {
"clip_id": "uploaded_clip_id",
"status": "complete"
},
"clips": [
{
"song_id": "new_clip_id",
"song_title": "Cover version",
"status": "pending"
}
]
}
}
Large files and truncated requests
Recommended: split upload and cover into two steps
The one-shot endpoint (this one) merges upload and generation into a single step: the large body sits in that one and only step, so once it breaks you neither know whether anything was generated nor whether you were charged.
| Step 1 upload | Step 2 cover | |
|---|---|---|
| Endpoint | POST /api/music/upload-source |
POST /api/music/generate-from-source |
| Billing | Free (upload only, no generation) | 0.6 ¥ per call; 1.2 ¥ per call with Max Mode |
| Request body | The audio itself (base64 / multipart), or just a link via file_url |
About 250 bytes (just one sourceClipId) |
| Returns | clip_id (the source audio's clip ID) |
data.clips[].song_id (the generated result) |
| If it fails | Just retry, no charge and no generation task | Set the client timeout to ≥300 s; on timeout check with /api/music/songs first and do not retry immediately |
| Reusing the source material | The same clip_id also works for extend / replace / separation |
The audio must be re-uploaded every time |
Conclusion: audio over about 8 MB, an unstable network, or the same material used several times → use the two-step form; tiny audio on a stable network → this endpoint is less hassle.
What 400 unexpected end of JSON input means
It means the JSON the server received is incomplete (it is not a wrong parameter, a permission problem or a balance problem). The usual causes, most likely first:
- A large body truncated mid-transfer: the ingress layer records both "how many bytes the client declared" and "how many actually arrived". Measured case: the client declared 14.17 MB but only 11.8–12.4 MB arrived, and all three requests stopped at about 1.6–1.7 s; the missing byte count differed every time, which points at a broken transfer rather than a fixed limit (the server does not cap the request body size).
Content-Lengthnot matching the bytes actually sent: in Node.js, using a string's.length(UTF-16 character count) as the length miscalculates whenever the content contains Chinese or other multi-byte text.- Writing the body as a string instead of a Buffer, so the bytes written do not match what was declared.
All three happen before billing, so no charge is produced.
Client self-check list
-
Content-Lengthcomes fromBuffer.byteLength(json, 'utf8')(orBuffer.from(json).length) — neverjson.length; - The request body is written as a Buffer, not as a string;
- The socket / request timeout is ≥ 300 seconds;
- When the audio is over about 8 MB: use
file_url(the body is only a few hundred bytes), or use the multipart upload ofupload-source, or go straight to the two-step form; - For multipart requests, do not set
Content-Typemanually (let the client add the boundary); - Print the "bytes actually sent" and compare it with
Content-Length; - In the two-step form, confirm step 1 returned a
clip_idwith a completedstatusbefore sending step 2.
Common error reference
| Error | Meaning | What to do |
|---|---|---|
unexpected end of JSON input |
Request body incomplete (truncated) | Work through the self-check above, or switch to the two-step form |
invalid character '-' in numeric literal |
A multipart body was sent to a JSON endpoint | Do not use multipart on JSON endpoints, or do not set Content-Type manually |
one of file_path, file_base64, file_url, or multipart file is required |
No audio was supplied | Add file_base64 / file_url / a multipart file |
model is required |
The body was empty valid JSON (for example {}) |
Send at least model |
Invalid request: Invalid request, ... |
Wrapper message for a body that failed to parse | Read the second half for the concrete reason (usually one of the above) |
Related endpoints: upload source audio API (step 1), cover from a source clip API (step 2).
Get an API key
Create an API key in the console after signing up. ¥0.6 per run returns 2 tracks, downloads are unlimited, and no phone number is required.
Get API key Back to overview