Suno API
RouteAPI provides a unified REST API for Suno AI music generation. Generate music from text, extend songs, clone voices, separate stems, and more.
Workflow Overview
Section titled “Workflow Overview”Suno music generation is asynchronous. The typical flow:
- Submit a task to
/suno/audios→ receive atask_id - Wait 2-3 seconds before starting to poll
- Poll every 3-5 seconds via
/suno/taskswithaction: "retrieve" - When status becomes
success→ fetch theaudio_url
Task Status
Section titled “Task Status”| Status | Meaning | Next Step |
|---|---|---|
submitted | Queued for processing | Continue polling |
queued | Waiting in queue | Continue polling |
in_progress | Generating | Continue polling |
success | ✅ Complete | Read audio_url from the first item in data |
failure | ❌ Failed | Check error field |
Quick Start
Section titled “Quick Start”Step 1: Submit Task
Section titled “Step 1: Submit Task”curl -X POST "https://api.routeapi.ai/suno/audios" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "chirp-v5-5", "prompt": "A cheerful summer pop song with piano and guitar", "async": true }'Response:
{ "success": true, "task_id": "task_xxxxx" }Step 2: Poll for Result
Section titled “Step 2: Poll for Result”Poll every 3-5 seconds until status becomes success:
curl -X POST "https://api.routeapi.ai/suno/tasks" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "action": "retrieve", "id": "task_xxxxx" }'Step 3: Get Audio
Section titled “Step 3: Get Audio”{ "id": "task_xxxxx", "task_id": "task_xxxxx", "status": "success", "data": [ { "id": "audio_1", "audio_url": "https://cdn.suno.ai/xxx.mp3", "video_url": "https://cdn.suno.ai/xxx.mp4", "title": "Summer Breeze", "duration": 183.5 } ]}Supported Models
Section titled “Supported Models”| Model ID | Max Duration | Lyric Limit | Style Limit | Title Limit |
|---|---|---|---|---|
chirp-v5-5 | 8 minutes | 5000 chars | 1000 chars | 100 chars |
chirp-v5 | 8 minutes | 5000 chars | 1000 chars | 100 chars |
chirp-v4-5-plus | 8 minutes | 5000 chars | 1000 chars | 100 chars |
chirp-v4-5 | 4 minutes | 5000 chars | 1000 chars | 100 chars |
chirp-v4 | 150 seconds | 3000 chars | 200 chars | 80 chars |
chirp-v3-5 | 120 seconds | 3000 chars | 200 chars | 80 chars |
The prompt field (non-custom mode) has a uniform 500 character limit across all models.
POST /suno/audios
Section titled “POST /suno/audios”The core endpoint for audio operations: the action field selects the operation, and the request body carries the fields that action needs.
Core Parameters
Section titled “Core Parameters”| Field | Type | Default | Description |
|---|---|---|---|
model | string | chirp-v4 | Generation model, recommend chirp-v5-5 |
action | string | generate | Operation type (see below) |
async | boolean | — | Strongly recommended true to avoid timeouts. Only an explicit false selects the synchronous branch; when the field is omitted it is not forwarded, so the upstream default applies |
Action Types
Section titled “Action Types”| Action | Description | Required Fields |
|---|---|---|
generate | Generate new music from prompt | prompt or custom=true + lyric + style |
extend | Extend existing audio | audio_id + continue_at |
concat | Concatenate audio clips into full track | audio_id |
cover | Re-interpret existing song in new style | audio_id |
upload_cover | Cover uploaded audio | audio_id (via /suno/upload) |
upload_extend | Extend uploaded audio | audio_id + continue_at |
artist_consistency | Sing with specified Persona | persona_id + lyric / prompt |
artist_consistency_vox | VOX mode Persona singing (higher quality) | persona_id + lyric / prompt |
stems | Separate into vocals + instrumental | audio_id |
all_stems | Separate into all tracks (vocals, drums, bass, other) | audio_id |
replace_section | Replace audio segment | audio_id + replace_section_start + replace_section_end |
underpainting | Add AI instrumental to vocal track | audio_id + underpainting_start + underpainting_end |
overpainting | Add AI vocals to instrumental track | audio_id + overpainting_start + overpainting_end |
samples | Add AI samples in time range | audio_id + samples_start + samples_end |
remaster | Remaster audio to improve quality | audio_id |
mashup | Merge multiple songs | mashup_audio_ids (array) |
inspo | Generate music inspired by 1-4 reference tracks | audio_urls (1-4 URLs) |
Content Parameters
Section titled “Content Parameters”| Field | Type | Description |
|---|---|---|
prompt | string | Natural language description of music theme, mood, scene |
lyric | string | Custom lyrics with section markers like [Verse], [Chorus], [Bridge] |
custom | boolean | Enable custom mode (allows specifying lyrics, title, style) |
instrumental | boolean | Generate instrumental music (no vocals) |
title | string | Song title |
style | string | Style/genre/mood description, comma-separated |
style_negative | string | Unwanted styles (e.g., “heavy, rock, guitar”) |
duration | integer | Desired duration in seconds (10-360), hint only in custom mode |
Audio Operation Parameters
Section titled “Audio Operation Parameters”| Field | Type | Description |
|---|---|---|
audio_id | string | Existing audio ID (from generation or /suno/upload) |
audio_urls | string[] | Reference audio public URLs (1-4) |
mashup_audio_ids | string[] | Source audio IDs for mashup |
continue_at | number | Extend start time (seconds) |
Voice & Persona Parameters
Section titled “Voice & Persona Parameters”| Field | Type | Description |
|---|---|---|
persona_id | string | Voice persona ID (created via /suno/persona) |
vocal_gender | string | Vocal gender preference: f (female) / m (male) |
variation_category | string | Remaster precision: high / normal / subtle |
Examples
Section titled “Examples”Simple Generation
Section titled “Simple Generation”curl -X POST "https://api.routeapi.ai/suno/audios" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "chirp-v5-5", "prompt": "Uplifting electronic dance music with synth lead", "async": true }'Custom Lyrics
Section titled “Custom Lyrics”curl -X POST "https://api.routeapi.ai/suno/audios" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "chirp-v5-5", "custom": true, "title": "Summer Dreams", "style": "pop, upbeat, piano", "lyric": "[Verse]\nWalking on the beach at sunset\n[Chorus]\nSummer dreams never fade", "async": true }'Extend Song
Section titled “Extend Song”curl -X POST "https://api.routeapi.ai/suno/audios" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "model": "chirp-v5", "action": "extend", "audio_id": "abc123-audio-id", "continue_at": 30.5, "async": true }'Separate Stems
Section titled “Separate Stems”curl -X POST "https://api.routeapi.ai/suno/audios" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "action": "stems", "audio_id": "song-to-split", "async": true }'POST /suno/tasks
Section titled “POST /suno/tasks”Query task status and results. This is the main way to retrieve results from /suno/audios. POST /suno/fetch (batch) and GET /suno/fetch/{id} return the same records in a different envelope, and if you passed callback_url when submitting, RouteAPI also POSTs a completion payload (task_id, status, fail_reason, data) to that URL once the task reaches a terminal state.
Query Single Task
Section titled “Query Single Task”curl -X POST "https://api.routeapi.ai/suno/tasks" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "action": "retrieve", "id": "task_xxxxx" }'Response:
{ "id": "task_xxxxx", "task_id": "task_xxxxx", "status": "success", "data": [ { "id": "audio_1", "audio_url": "https://cdn.suno.ai/xxx.mp3", "video_url": "https://cdn.suno.ai/xxx.mp4", "title": "Song Title", "text": "Lyrics content", "model_name": "chirp-v5-5", "duration": 183.5 } ]}The task record’s data holds the upstream result. For song generation it is usually an array of clips; some operations (for example /suno/upload) return a single object instead, so read data as “an array or one object” rather than always indexing it. Terminal records also carry created_at, started_at, finished_at, elapsed, the sanitized response, and the submitted request.
Batch Query
Section titled “Batch Query”curl -X POST "https://api.routeapi.ai/suno/tasks" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "action": "retrieve_batch", "ids": ["task_001", "task_002"] }'POST /suno/lyrics
Section titled “POST /suno/lyrics”Generate lyrics that can be used in /suno/audios’s lyric field.
| Field | Type | Required | Description |
|---|---|---|---|
prompt | string | ✅ | Lyrics creation prompt |
model | string | Default default |
curl -X POST "https://api.routeapi.ai/suno/lyrics" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "prompt": "Write upbeat summer pop lyrics with chorus" }'POST /suno/upload
Section titled “POST /suno/upload”Upload external audio file, returns audio_id for use in extend, cover, voice cloning operations.
curl -X POST "https://api.routeapi.ai/suno/upload" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "audio_url": "https://example.com/my-audio.mp3" }'GET/POST /suno/persona
Section titled “GET/POST /suno/persona”Manage voice personas (voice cloning).
GET — List Personas
Section titled “GET — List Personas”curl -X GET "https://api.routeapi.ai/suno/persona" \ -H "Authorization: Bearer $ROUTEAPI_KEY"POST — Create Persona
Section titled “POST — Create Persona”curl -X POST "https://api.routeapi.ai/suno/persona" \ -H "Authorization: Bearer $ROUTEAPI_KEY" \ -H "Content-Type: application/json" \ -d '{ "audio_id": "uploaded-audio-id", "name": "My Voice", "vocal_start": 5.0, "vocal_end": 15.0 }'Other Endpoints
Section titled “Other Endpoints”| Endpoint | Method | Description |
|---|---|---|
/suno/voices | POST | Extract vocal information from audio |
/suno/style | POST | Generate style description from prompt |
/suno/mashup-lyrics | POST | Merge two lyrics |
/suno/wav | POST | Export WAV format (30-day validity) |
/suno/mp4 | POST | Export MP4 video |
/suno/midi | POST | Export MIDI file |
/suno/timing | POST | Get lyrics timing for subtitles |
/suno/vox | POST | Extract vocal segment (remove instrumental) |
Errors
Section titled “Errors”Errors after the request reaches the task handler use the task body:
{ "code": "invalid_request", "message": "prompt_empty (request id: ...)", "data": null }Authentication and routing failures happen before that handler and keep the OpenAI-style body {"error": {"message": "...", "type": "...", "code": "..."}}. See Common errors.
| Status | Code | Cause |
|---|---|---|
| 400 | invalid_request | A required field for the endpoint or action is missing; message names it (for example prompt_empty for /suno/lyrics, or audio_id_vocal_start_vocal_end_required for /suno/vox) |
| 400 | task_not_exist | action: "retrieve" with an id that is not one of your tasks |
| 401 | — | The API key is missing, invalid, or expired (OpenAI-style body) |
| 403 | insufficient_user_quota | The account balance or key quota cannot cover the pre-charge |
| 429 | — | The upstream channel reported a rate limit or saturation; RouteAPI replaces the provider’s raw message |
| 502 | acedata_suno_request_failed | The upstream accepted the call but rejected it: content moderation, unknown audio_id, unsupported action. message carries the upstream explanation |
| 502 | upstream_task_id_missing | The upstream returned a 2xx without a task ID, so the task could not be polled; the pre-charged amount is refunded |
| 503 | get_channel_failed | The channel behind the task hit a hard fault, such as an unusable upstream account; RouteAPI replaces the provider’s raw message. (A model with no channel at all is rejected before the task handler with 503 model_not_found and the OpenAI-style body) |
| 4xx / 5xx | fail_to_fetch_task | The upstream returned a non-2xx status; the status and body are forwarded, and the pre-charge is refunded |
A failed task (polling returns failure) also refunds the pre-charged amount; its text is in the record’s error and message fields.
Best Practices
Section titled “Best Practices”Polling Code Example (JavaScript)
Section titled “Polling Code Example (JavaScript)”async function generateAndPoll(prompt) { // Submit task const res = await fetch('https://api.routeapi.ai/suno/audios', { method: 'POST', headers: { 'Authorization': 'Bearer $ROUTEAPI_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'chirp-v5-5', prompt, async: true }) }); const { task_id } = await res.json();
// Wait before first poll await new Promise(r => setTimeout(r, 3000));
const deadline = Date.now() + 3 * 60 * 1000; // 3 min timeout while (Date.now() < deadline) { const poll = await fetch('https://api.routeapi.ai/suno/tasks', { method: 'POST', headers: { 'Authorization': 'Bearer $ROUTEAPI_KEY', 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'retrieve', id: task_id }) }); const result = await poll.json();
if (result.status === 'success') return result.data; if (result.status === 'failure') throw new Error(result.error || result.message);
await new Promise(r => setTimeout(r, 4000)); // Poll every 4s } throw new Error('Task timeout');}Q: Which model should I use?
Use chirp-v5-5 (latest and most capable). For stable older version, choose chirp-v4.
Q: How long does generation take?
generate usually 15-40 seconds. Processing operations like stems, all_stems, remaster may take 1-3 minutes.
Q: Can I use audio_url directly?
Yes, the CDN URL is ready for playback or download: curl -o song.mp3 "<audio_url>"
Q: How long is the track a single generate produces?
Usually 2-3 minutes of audio. Use extend to continue to 4-5+ minutes by setting continue_at.
Q: Common failure reasons?
message / code | Cause | Solution |
|---|---|---|
prompt_empty (invalid_request) | /suno/lyrics or /suno/style without prompt | Provide the prompt |
audio_id_required (invalid_request) | /suno/wav, /suno/mp4, /suno/midi, or /suno/timing without audio_id | Pass the audio ID returned by a generation |
acedata_suno_request_failed (502) | The upstream rejected the submitted task, commonly content moderation or an unknown audio_id | Read the upstream text in message, adjust the request, and retry |
Polled status failure | The task failed after it was accepted; the reason is in the record’s error / message | Adjust the prompt and retry; the pre-charge was refunded |
insufficient_user_quota (403) | The account balance or key quota cannot cover the pre-charge | Top up the account or raise the key quota |
Q: Concurrent request limits?
Yes, depends on account plan. Recommended concurrency ≤ 5, polling interval ≥ 3s.