Simple mode generation API
Endpoint description
Simple mode generation is for turning a one-sentence description into a complete song. The service writes the lyrics, melody, arrangement and vocals from your description.
Endpoint: POST /api/music/create
Billing: Paid, currently ¥0.6 per call by default. Each call usually returns 2 songs; 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.
Recommended model: suno-v6
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.
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 |
|---|---|---|---|---|
| description | string | yes | - | Song description, any language; at most 3000 characters (longer returns 400 and is not charged) |
| model | string | no | suno-v6 | Customer-facing model name: suno-v6, suno-v6-wild or suno-v6-mini |
| instrumental | boolean | no | false | Whether the song is instrumental only |
| wait_completion | boolean | no | false | Whether to wait for generation to finish before responding |
| references | array | no | - | Reference material list, which may carry reference songs / legacy voices / reference images; at most 5 songs, 1 voice and 1 image. See "Reference material references (optional)" below |
Request examples
curl -X POST https://www.suno-api.io/api/music/create \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-v6",
"description": "an upbeat Mandarin pop song about summer by the sea",
"instrumental": false,
"wait_completion": false
}'
Reference material references (optional)
references is an array that attaches "reference material" to this generation. It is entirely optional (leaving it out behaves exactly as before), and you may attach only one kind or any combination.
Element types
| type | Required field | Optional fields | Maximum | Notes |
|---|---|---|---|---|
song |
song_id |
style_prompt, lyrics, duration_s |
5 | Reference song; song_id comes from the song query and history API and the song must already be completed |
voice |
voice_id |
- | 1 | Legacy voice; voice_id comes from the voice clone and generate-with-voice API |
image |
base64 |
- | 1 | Reference image; send base64 content in the form data:image/jpeg;base64,... |
Rules
- The order of the reference songs is their semantic order: pass them in the order you intend (for example "melody from the first, vocals from the second").
style_prompt/lyrics/duration_smay all be omitted; the platform fills them in from the song information, and anything you send wins.- For pure instrumental songs,
lyricsmay be""or"[Instrumental]". - Reference images support
jpeg/png/webp, up to 10MB each, submitted with the request body as base64. - When attaching several reference songs, also use
accept_mode=async(see "Accept-receipt mode" above): the service has to prepare the reference material first, so a synchronous wait can be long.
Example: two reference songs
curl -X POST https://www.suno-api.io/api/music/create \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-v6",
"description": "combine these ideas into one song",
"references": [
{ "type": "song", "song_id": "43eb711d-81f8-4e72-a68e-b2498bac88f9" },
{ "type": "song", "song_id": "a90438d2-35fc-4443-88f4-56531e6e5d24" }
]
}'
Example: reference song + legacy voice
{
"model": "suno-v6",
"description": "sing these ideas with this voice",
"references": [
{ "type": "song", "song_id": "43eb711d-81f8-4e72-a68e-b2498bac88f9" },
{ "type": "voice", "voice_id": "5813cbe1-36f1-414a-a9a8-4d399dfeb292" }
]
}
Example: reference song + reference image
{
"model": "suno-v6",
"description": "capture the mood of this image",
"references": [
{ "type": "song", "song_id": "43eb711d-81f8-4e72-a68e-b2498bac88f9" },
{ "type": "image", "base64": "data:image/jpeg;base64,/9j/4AAQ..." }
]
}
Parameter errors
These cases return HTTP 400, blocked before any billing, so they cost nothing:
| Case | Error message |
|---|---|
| More than 5 reference songs | references supports at most 5 song references |
| More than 1 voice | references supports at most 1 voice reference |
| More than 1 reference image | references supports at most 1 image reference |
Invalid type |
references[0].type must be one of song, voice, image |
Song missing song_id |
references[0].song_id is required |
| Song not finished yet | references[0].song_id must be a real completed clip |
| The same song passed twice | references[0].song_id is duplicated |
The number in
references[0]is that element's index in the array, so the failing item is easy to locate.
Response
An array of songs, usually holding 2 tasks or songs.
[
{
"song_id": "clip_id",
"song_title": "Summer Sea Breeze",
"status": "pending",
"description": "an upbeat Mandarin pop song about summer by the sea",
"audio_url": "",
"cover_url": "",
"video_url": "",
"model_version": "suno-v6",
"generation_type": "generate",
"created_at": "2026-05-21T10:00:00Z"
}
]
Accept-receipt mode (optional, recommended for clients that should not hold long connections)
By default this endpoint is synchronous: the response arriving is what means the order was accepted, and measured waits can reach about 230 seconds. If your client should not hold a long connection, turn on accept-receipt mode — either one works:
- add
"accept_mode": "async"to the request body - add the header
X-Accept-Mode: async
With it on, the endpoint returns HTTP 202 immediately, meaning only "accepted and queued for generation", while generation continues in the background:
{
"success": true,
"data": {
"request_id": "ac_20260920092714315816800d76fv3nq",
"status": "queued",
"accepted_at": 1789896434,
"pre_consumed_quota": 300000,
"tasks": [
{ "index": 0, "status": "queued" },
{ "index": 1, "status": "queued" }
]
}
}
The accept stage does not return a
song_id. No real song ID exists yet, and neitherrequest_idnortasks[].indexis a song ID — do not save them assong_id. The realsong_idappears in the progress query below.
Query the accept progress
Endpoint: GET /api/music/requests/{request_id} (free, authenticated with the same API key)
curl "https://www.suno-api.io/api/music/requests/ac_20260920092714315816800d76fv3nq" \
-H "Authorization: Bearer YOUR_API_KEY"
{
"success": true,
"data": {
"request_id": "ac_20260920092714315816800d76fv3nq",
"status": "processing",
"tasks": [
{ "index": 0, "status": "processing", "song_id": "f3fa09f5-3d55-4953-826c-0d23f043ec57" },
{ "index": 1, "status": "processing", "song_id": "5fcafe3e-3fae-4d64-8f66-c01bc027b0c3" }
]
}
}
What each status value means:
| status | Meaning |
|---|---|
queued |
Accepted, not yet tied to specific songs (tasks may be an empty array) |
processing |
Submitted for generation |
partial |
One of the two has completed |
completed |
Both completed, tasks[].song_id is readable |
failed |
Submission failed, the charge has been fully refunded, and error_message gives the reason |
Each task also has its own tasks[].status, with the same values as the song query API:
pending (submitted, generating), processing, completed, failed. Task items may also carry title,
duration and an already registered audio_url; anything missing simply means it is not generated yet.
Once you have the real song_id, continue with the song query API or the download endpoints.
Python example
import time
import requests
BASE = "https://www.suno-api.io"
HEADERS = {"Authorization": "Bearer YOUR_API_KEY", "Content-Type": "application/json"}
accepted = requests.post(
f"{BASE}/api/music/create",
headers={**HEADERS, "Idempotency-Key": "your-own-unique-key"},
json={"description": "an upbeat pop song", "model": "suno-v6", "accept_mode": "async"},
timeout=300,
).json()["data"]
request_id = accepted["request_id"]
while True:
time.sleep(5)
progress = requests.get(
f"{BASE}/api/music/requests/{request_id}", headers=HEADERS, timeout=60
).json()["data"]
if progress["status"] in ("completed", "failed"):
print(progress)
break
Compatibility and notes
- Without
accept_mode, behaviour is exactly as before: synchronous wait, returning200and a song array. Existing integrations need no code change. - This switch currently works only on
POST /api/music/createandPOST /api/music/create/custom; other generation endpoints do not support it yet. - Also send an
Idempotency-Key(your own unique string, ≤128 characters): submitting the same key twice is accepted only once, a replay returns the same receipt, and nothing is charged twice. This matters even more in asynchronous mode. - With
accept_mode=async,wait_completionis ignored (the receipt returns immediately without waiting for songs). - The charge is still pre-consumed when the order is accepted; a failed generation is refunded in full automatically.
- Text field limit:
descriptionholds at most 3000 characters (counted in Unicode characters, one Han character counting as 1). Longer input returns HTTP400directly, never enters generation and is not charged, and the error message carries the measured length, for exampledescription must not exceed 3000 characters; received 4120. This endpoint has no lyrics/style fields; for those, see "Text field limit" in the Custom mode generation API. The general conventions are in "Text field limits" in the New API overview. - Without
references, behaviour, price and response are exactly as before: existing integrations need no code change. - When reference material is attached (especially several reference songs), prefer
accept_mode=asyncaccept-receipt mode over a long synchronous wait; how long the material takes to prepare depends on its amount and source.
Next steps
If the songs are not finished when the response arrives, use the song query API to fetch the final result.
Node/Electron integration example
If you are integrating standard generation inside an Electron main process, a Node.js server or a desktop client, this is the recommended flow:
- Call
POST /api/music/createto create the generation task. - Read
song_idoridfrom the response. - Call
POST /api/music/queryevery 2 seconds to check the status. - Treat generation as finished once
audio_urlappears in the query result.
Stop using the old paths POST /api/generate or GET /api/clip?id=...; those are not part of the currently recommended public integration surface.
const http = require('http');
const https = require('https');
const BASE_URL = 'https://www.suno-api.io';
const MODEL = 'suno-v6';
const POLL_INTERVAL_MS = 2000;
const MAX_POLL_ATTEMPTS = 60;
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
function requestJson(urlString, apiKey, body) {
const url = new URL(urlString);
const transport = url.protocol === 'https:' ? https : http;
const payload = JSON.stringify(body);
return new Promise((resolve, reject) => {
const req = transport.request(
{
method: 'POST',
hostname: url.hostname,
port: url.port || undefined,
path: `${url.pathname}${url.search}`,
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload),
},
},
(res) => {
let raw = '';
res.setEncoding('utf8');
res.on('data', (chunk) => {
raw += chunk;
});
res.on('end', () => {
const data = raw ? JSON.parse(raw) : {};
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
reject(new Error(`Suno API request failed: HTTP ${res.statusCode} ${raw}`));
return;
}
resolve(data);
});
}
);
req.on('error', reject);
req.write(payload);
req.end();
});
}
function getPayload(response) {
return response && response.data !== undefined ? response.data : response;
}
function toSongList(payload) {
if (Array.isArray(payload)) return payload;
if (Array.isArray(payload?.songs)) return payload.songs;
if (Array.isArray(payload?.clips)) return payload.clips;
return payload ? [payload] : [];
}
function collectSongIds(createResponse) {
return toSongList(getPayload(createResponse))
.map((song) => song.song_id || song.id)
.filter(Boolean);
}
async function generateSunoSong({ apiKey, description, instrumental = false }) {
const createResponse = await requestJson(`${BASE_URL}/api/music/create`, apiKey, {
description,
model: MODEL,
instrumental,
wait_completion: false,
});
const songIds = collectSongIds(createResponse);
if (songIds.length === 0) {
throw new Error(`Suno API did not return song ids: ${JSON.stringify(createResponse)}`);
}
for (let attempt = 0; attempt < MAX_POLL_ATTEMPTS; attempt += 1) {
await sleep(POLL_INTERVAL_MS);
const queryResponse = await requestJson(`${BASE_URL}/api/music/query`, apiKey, {
song_ids: songIds,
model: MODEL,
});
const songs = toSongList(getPayload(queryResponse));
const completed = songs.find((song) => song.audio_url);
if (completed) {
return {
audioUrl: completed.audio_url,
title: completed.song_title || completed.title || 'Suno Song',
duration: completed.duration,
};
}
const allFailed = songs.length > 0 && songs.every((song) =>
['failed', 'error', 'rejected'].includes(String(song.status || '').toLowerCase())
);
if (allFailed) {
throw new Error(`Suno generation failed: ${JSON.stringify(songs)}`);
}
}
throw new Error('Suno generation timed out after 120 seconds. Please query again later.');
}
// Usage:
// generateSunoSong({
// apiKey: process.env.SUNO_API_KEY,
// description: 'A bright pop song about the ocean in summer',
// }).then(console.log).catch(console.error);
In real projects keep the API key in a server environment variable, a desktop client setting or secure storage — never in front-end code and never committed to a repository.
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