API reference
Suno music
Asynchronous Suno API — POST /suno/submit/music writes songs, POST /suno/submit/lyrics writes lyrics, GET /suno/fetch/{task_id} returns the result. SunoAPI-compatible; examples for inspiration mode, custom mode and extending a song, with per-call pricing.
Suno works as an asynchronous task API: submit a task, get its ID, then poll until generation finishes. Paths and fields follow the SunoAPI format, so existing code only needs https://www.tutujin.com as its address and a key from this site.
https://www.tutujin.com/suno/submit/musicHeaders
| Header | Required | Description |
|---|---|---|
Authorization | Yes | Bearer sk-..., an API key from the console |
Content-Type | Yes | application/json |
The body has no model field; each endpoint is billed as its own model:
| Endpoint | Purpose | Billed as |
|---|---|---|
POST /suno/submit/music | Write songs, two per call | suno_music |
POST /suno/submit/lyrics | Write lyrics | suno_lyrics |
GET /suno/fetch/{task_id} | Get one task | Free |
POST /suno/fetch | Get several tasks with {"ids": [...]} | Free |
Request body
/suno/submit/music takes either a one-sentence description (inspiration mode) or your own lyrics, title and style (custom mode).
| Parameter | Type | Description |
|---|---|---|
gpt_description_prompt | string | Inspiration mode: describe the song in one sentence; lyrics and style are generated |
prompt | string | Custom mode: the full lyrics. For /suno/submit/lyrics, the topic |
mv | string | Suno model version, e.g. chirp-v4. Without it an older default is used, so always set it |
title | string | Song title (custom mode) |
tags | string | Style tags such as pop, piano (custom mode) |
make_instrumental | boolean | true for instrumental music without vocals |
task | string | extend to continue a song |
continue_clip_id | string | Clip ID of the song to continue |
continue_at | number | Where to continue from, in seconds |
Request example
curl https://www.tutujin.com/suno/submit/music \
-H "Authorization: Bearer $TUTUJIN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"gpt_description_prompt": "An upbeat summer pop song about the beach, female vocals",
"mv": "chirp-v4",
"make_instrumental": false
}'
# poll with the task ID returned in "data"
curl https://www.tutujin.com/suno/fetch/$TASK_ID \
-H "Authorization: Bearer $TUTUJIN_API_KEY"import os
import time
import requests
BASE = "https://www.tutujin.com"
HEADERS = {"Authorization": "Bearer " + os.environ["TUTUJIN_API_KEY"]}
resp = requests.post(
f"{BASE}/suno/submit/music",
headers=HEADERS,
json={"gpt_description_prompt": "An upbeat summer pop song about the beach, female vocals", "mv": "chirp-v4", "make_instrumental": False},
timeout=60,
)
resp.raise_for_status()
task_id = resp.json()["data"]
while True:
time.sleep(10)
task = requests.get(f"{BASE}/suno/fetch/{task_id}", headers=HEADERS, timeout=30).json()["data"]
if task["status"] in ("SUCCESS", "FAILURE"):
break
print(task["status"], task.get("fail_reason"))
for clip in task["data"] or []:
print(clip.get("title"), clip.get("audio_url"))const BASE = 'https://www.tutujin.com'
const headers = { Authorization: `Bearer ${process.env.TUTUJIN_API_KEY}`, 'Content-Type': 'application/json' }
const submit = await fetch(`${BASE}/suno/submit/music`, {
method: 'POST',
headers,
body: JSON.stringify({ gpt_description_prompt: 'An upbeat summer pop song about the beach, female vocals', mv: 'chirp-v4', make_instrumental: false }),
}).then((r) => r.json())
let task
do {
await new Promise((r) => setTimeout(r, 10000))
task = (await fetch(`${BASE}/suno/fetch/${submit.data}`, { headers }).then((r) => r.json())).data
} while (!['SUCCESS', 'FAILURE'].includes(task.status))
console.log(task.status, task.fail_reason)
for (const clip of task.data ?? []) console.log(clip.title, clip.audio_url)A song usually takes one to a few minutes; polling every 10 seconds or so is enough. For lyrics only, call /suno/submit/lyrics with {"prompt": "topic"}; the result is in data.data.text.
Response example
Submitted:
{ "code": "success", "data": "task_xxxxxxxxxxxxxxxx", "message": "" }Task (songs):
{
"code": "success",
"data": {
"task_id": "task_xxxxxxxxxxxxxxxx",
"status": "SUCCESS",
"fail_reason": "",
"data": [
{ "id": "…", "title": "Summer Breeze", "audio_url": "https://…/song.mp3", "image_url": "https://…/cover.jpeg" },
{ "id": "…", "title": "Summer Breeze", "audio_url": "https://…/song.mp3", "image_url": "https://…/cover.jpeg" }
]
}
}| Field | Description |
|---|---|
data (submit) | Task ID |
data.status | NOT_START, SUBMITTED, QUEUED, IN_PROGRESS, SUCCESS, FAILURE |
data.fail_reason | Why the task failed |
data.data | Songs: an array of two clips (audio_url audio, image_url cover). Lyrics: an object with title and text |
Billing
- Per call:
/suno/submit/musicis billed assuno_music(two songs per call) and/suno/submit/lyricsassuno_lyrics; prices are in the list below. - A rejected submit costs nothing and a task that ends in failure is refunded. Polling is free.
Notes
- The API is asynchronous: store the task ID and poll, or check back later, instead of holding one request open.
- Only songs and lyrics are available for now; concatenation, stems, audio upload and other Suno features are not.
- Audio and cover URLs are for short-term use; download them into your own storage.
- Lyrics or descriptions that trip moderation make the task fail. Change them and submit again.
Models that support this endpoint
| Model | Vendor | Cheapest group | Input ¥/1M | Output ¥/1M | Per request ¥ |
|---|---|---|---|---|---|
| Suno Lyrics | Suno | Standard | — | — | ¥0.02 |
| Suno Music | Suno | Standard | — | — | ¥1 |
Prices in CNY, cheapest group for each model; updated Nov 13, 2026, 6:08 PM.
Still stuck? Add our WeChat support JsnonoChat and send the request id from the error message, and we will look into it.