Skip to content

UlazAI developer docs

Suno API

One REST API for Suno music generation: submit a job, poll it, download the audio. UlazAI exposes 23 operations on six Suno models, priced in purchased credits, with idempotent submission and private storage for generated media. UlazAI is an independent integration, not Suno’s official API. The catalog route returns the same field specs as data.

Base URL
https://ulazai.com/api/v1/music/studio
Authentication
API key in the Authorization: Bearer header. The catalog needs no key.
Request format
JSON with snake_case field names, or multipart for uploads.
Credits
Purchased credits only. Failed jobs are refunded automatically.

To call the API you need a verified email address, an API key from your dashboard and credits from the credit packages page. Prefer pointing and clicking? The Music Studio is the same engine without code, and the older audio docs stay available for the legacy music routes.

Moonlit platform · Suno V5.5
One of the two takes returned by the example below. Generated and downloaded through this API.

Download this instrumental (MP3)

Three calls to your first song

Generate a 20-second instrumental with Suno V5.5. The complete examples below handle submission, polling and downloading for you.

  1. Submit the song. Send POST /jobs/ with your title, style and a request UUID. Save the returned job ID. Reuse that UUID when retrying the same request.
  2. Wait for the audio. Check GET /jobs/{id}/ every five seconds until the status is completed. Stop on failed or awaiting_user.
  3. Download each take. Use each track's audio_url. Authenticate the UlazAI request, then follow the signed download URL without forwarding your API key.

Choose a complete curl, Python or Node example

Example response after submission

A successful submit returns 201 with the new job. The track list fills when generation completes.

{
  "success": true,
  "data": {
    "id": "3c605fd4-f298-4d19-b5c3-0683d1afa6f7",
    "status": "processing",
    "operation": "generate",
    "model": "V5_5",
    "credits_used": 12,
    "tracks": []
  }
}

Generation takes minutes, not seconds. The job keeps running on the server even if you stop polling, so your script can hit its polling limit, walk away and pick the job up later with the same GET call.

Keep the API key on your server

The key authenticates every request as you and spends your credits. Read it from an environment variable in server-side code. A key embedded in a browser app, mobile app or public repository is a key anyone can use to drain your balance; rotate it in the dashboard the moment that happens.

Downloads redirect to a short-lived signed storage URL. Do not send your Authorization header to that host: the signed URL already carries its own credentials, and forwarding your key alongside it leaks the key to storage logs and can cause authentication failures. Resolve the redirect first, then fetch the target without the header. Python and Node resolve the redirect explicitly; curl automatically removes the header when the host changes.

Header formats and programmatic key creation are covered in the authentication docs.

Credits per operation

Music Studio and this API spend purchased credits only. Welcome credits cannot pay for music jobs. Credits are charged when the job is accepted, and a failed job is refunded automatically, exactly once, without a support request.

All 23 operations with their credit prices. The catalog route returns these values as data.
Operation Credits Notes
generate 12 Create a song
extend 12 Extend a track
needs a source job (see operation rules)
upload_cover 12 Cover uploaded audio
needs an upload
upload_extend 12 Extend uploaded audio
needs an upload
add_vocals 12 Add vocals
needs an upload
add_instrumental 12 Add instrumental
needs an upload
replace_section 6 Replace a section
needs a source take or an upload
mashup 12 Mashup two tracks
needs 2 uploads
stems 10
by mode: vocals + instrumental 10, all stems (up to 12) 50, one specific stem 20
Separate stems
needs a source take or an upload
wav 1 Convert to WAV
needs a source job (see operation rules)
midi 0 Generate MIDI
free, shares the 25-per-day free cap
needs a source job (see operation rules)
lyrics 1 Write lyrics
timestamped_lyrics 1 Timestamped lyrics
can complete during submission; inspect the returned status
needs a source job (see operation rules)
style_boost 1 Boost a style description
can complete during submission; inspect the returned status
cover_art 0 Cover art
free, shares the 25-per-day free cap
needs a source job (see operation rules)
music_video 2 Make a music video
needs a source job (see operation rules)
sounds 3 Generate a sound
recovery 0 Recover lost audio
free, shares the 25-per-day free cap
needs a source job (see operation rules)
voice_validate 0 Start custom voice
free, shares the 25-per-day free cap
needs an upload
voice_regenerate 0 New phrase for existing attempt
free, shares the 25-per-day free cap
needs a source job (see operation rules)
voice_generate 0 Finish custom voice
free, shares the 25-per-day free cap
needs a source job (see operation rules)
needs an upload
persona 0 Persona
free, shares the 25-per-day free cap
can complete during submission; inspect the returned status
needs a source job (see operation rules)
voice_check 0 Check custom voice
free, shares the 25-per-day free cap
can complete during submission; inspect the returned status
needs a source job (see operation rules)

Operations priced at 0 credits share one cap: 25 free jobs per account per day. Paid operations require sufficient purchased credits; provider availability can still limit requests.

Six Suno models

Pass the id in the model field. V5.5 is the default for new work and the only model that reads duration. V4 is the odd one out for style descriptions: it accepts 200 characters where every other model accepts 1000.

Model ids and what they are good at.
Id Model What to expect
V5_5 Suno V5.5 Newest model. Supports custom duration.
V5 Suno V5 Superior expression, faster generation.
V4_5PLUS Suno V4.5+ Richer sound, tracks up to 8 minutes.
V4_5ALL Suno V4.5 All Smart prompts, faster generations.
V4_5 Suno V4.5 Smart prompts, faster generations.
V4 Suno V4 Improved vocals, tracks up to 4 minutes. Style descriptions cap at 200 characters.

Seven routes, one namespace

Every Music Studio route under https://ulazai.com/api/v1/music/studio.
Method Route Purpose
GET https://ulazai.com/api/v1/music/studio/catalog/ Public. Models, operations, fields and credit prices. No key needed.
POST https://ulazai.com/api/v1/music/studio/jobs/ Submit an operation. Body: operation, request_id plus catalog fields.
GET https://ulazai.com/api/v1/music/studio/jobs/ Your library, 25 jobs per page. Pass ?cursor= with the next value to keep reading.
GET https://ulazai.com/api/v1/music/studio/jobs/<uuid>/ Job status and result. Poll this route.
PATCH https://ulazai.com/api/v1/music/studio/jobs/<uuid>/ Rename a track (title) or toggle is_favorite.
POST https://ulazai.com/api/v1/music/studio/upload/ Multipart upload of your own audio; returns the reference for the upload field.
GET https://ulazai.com/api/v1/music/studio/jobs/<uuid>/media/<kind>/<index>/ Private download. kind is track, image or asset; index is zero-based.

There are no customer webhooks. The provider callback route in this namespace is internal infrastructure with a signed token; you cannot register a URL and should not try to call it. Polling the job route is the supported integration path, and jobs also complete while you are not polling.

The catalog is open

GET https://ulazai.com/api/v1/music/studio/catalog/ needs no key and returns models, operations, field specs and credit prices. Machine-readable field specs mean a client can render its own forms without hardcoding this page. Pass ?language= with a supported language code to localise the labels.

curl --fail-with-body -sS "https://ulazai.com/api/v1/music/studio/catalog/"

# Optional: Dutch field labels.
curl --fail-with-body -sS "https://ulazai.com/api/v1/music/studio/catalog/?language=nl"

Submit a job

Every operation goes through POST https://ulazai.com/api/v1/music/studio/jobs/ with the same envelope: operation, request_id and the operation's fields in snake_case. This example body matches the complete scripts below:

{
  "operation": "generate",
  "request_id": "a20e890f-2e90-550b-9c28-1d48c531385c",
  "custom_mode": true,
  "instrumental": true,
  "title": "Moonlit platform",
  "style": "Gentle solo piano, warm analog texture",
  "model": "V5_5",
  "duration": 20
}

request_id makes retries safe

Generate a UUID once per logical submission and store it. Resending the request with the same request_id returns the original job instead of charging again, which is what you want after a timeout. Sending a different body under the same request_id fails with 409 request_id_conflict.

Order of checks, before any charge

  • Field validation against the catalog: types, limits, required and conditional fields.
  • Ownership checks: source_id and upload references must be jobs and files from your own library.
  • Prompt moderation on the text fields. Rejected prompts return 400 and nothing is charged.
  • Credit balance. Shortfalls return 402 insufficient_credits with the amount needed.

Derived operations build on earlier results. source_id is the UUID of a completed job in your library; audio_id is the id of one take or asset inside that job's result, found in its tracks[].id or assets[].id. The two are easy to mix up: source_id selects the job, audio_id selects the take.

Generate, poll, download

Each script submits the song, checks its status and downloads every take. Keep your API key in the ULAZAI_API_KEY environment variable on your server.

Create one request UUID before running a script. Keep it unchanged for retries:

export ULAZAI_REQUEST_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
curl · complete shell script

Requires bash, curl and jq.

#!/usr/bin/env bash
set -euo pipefail
: "${ULAZAI_API_KEY:?Set your API key in the environment}"
: "${ULAZAI_REQUEST_ID:?Set one UUID and keep it for retries}"
BASE=https://ulazai.com/api/v1/music/studio
# Requires curl and jq. Keep both the UUID and payload unchanged on retry.
payload=$(jq -n --arg id "$ULAZAI_REQUEST_ID" '{operation:"generate",request_id:$id,custom_mode:true,instrumental:true,title:"Moonlit platform",style:"Gentle solo piano, warm analog texture",model:"V5_5",duration:20}')
curl --fail-with-body -sS "$BASE/jobs/" \
  -H "Authorization: Bearer $ULAZAI_API_KEY" \
  -H "Content-Type: application/json" --data "$payload" > music-job.json
jq -e '.success == true' music-job.json >/dev/null
JOB_ID=$(jq -r '.data.id' music-job.json)
echo "Job: $JOB_ID"
for attempt in $(seq 1 120); do
  status=$(jq -r '.data.status' music-job.json)
  case "$status" in completed|failed|awaiting_user) break;; esac
  sleep 5
  curl --fail-with-body -sS "$BASE/jobs/$JOB_ID/" \
    -H "Authorization: Bearer $ULAZAI_API_KEY" > music-job.json
  jq -e '.success == true' music-job.json >/dev/null
done
if [ "$(jq -r '.data.status' music-job.json)" != completed ]; then
  echo "Job $JOB_ID is not completed. Resume with GET; keep the UUID for submit retries." >&2
  exit 1
fi
index=0
while IFS= read -r media_path; do
  case "$media_path" in
    /api/v1/music/studio/jobs/*/media/*) ;;
    *) echo "Legacy external media URL: download it separately without your API key." >&2; exit 1;;
  esac
  index=$((index + 1))
  # curl strips Authorization on cross-host redirects. Never add --location-trusted.
  curl --fail-with-body -sSL "https://ulazai.com$media_path" \
    -H "Authorization: Bearer $ULAZAI_API_KEY" -o "take-$index.mp3"
  echo "Saved take-$index.mp3"
done < <(jq -r '.data.tracks[].audio_url' music-job.json)
Python · standard library only

Requires Python 3. No packages to install.

import json
import os
import time
import urllib.error
import urllib.request
from pathlib import Path
from urllib.parse import urljoin, urlsplit

BASE = "https://ulazai.com/api/v1/music/studio/"
KEY = os.environ["ULAZAI_API_KEY"]
REQUEST_ID = os.environ["ULAZAI_REQUEST_ID"]  # keep this UUID for retries


def call(method, path, payload=None):
    request = urllib.request.Request(
        urljoin(BASE, path), method=method,
        data=json.dumps(payload).encode() if payload is not None else None,
        headers={"Authorization": "Bearer " + KEY, "Content-Type": "application/json", "User-Agent": "UlazAI-Music-Example/1.0"},
    )
    with urllib.request.urlopen(request, timeout=60) as response:
        body = json.load(response)
    if not body.get("success"):
        raise RuntimeError(body.get("error") or body.get("detail") or "API request failed")
    return body["data"]


payload = {
    "operation": "generate", "request_id": REQUEST_ID,
    "custom_mode": True, "instrumental": True,
    "title": "Moonlit platform", "style": "Gentle solo piano, warm analog texture",
    "model": "V5_5", "duration": 20,
}
job = call("POST", "jobs/", payload)
print("Job:", job["id"], flush=True)
for _ in range(120):
    if job["status"] in {"completed", "failed", "awaiting_user"}:
        break
    time.sleep(5)
    job = call("GET", "jobs/" + job["id"] + "/")
if job["status"] != "completed":
    raise RuntimeError(f"Job {job['id']}: {job['status']}. {job.get('error_message', '')} Resume with GET; keep the same request_id for submit retries.")


class NoRedirect(urllib.request.HTTPRedirectHandler):
    def redirect_request(self, *args, **kwargs):
        return None


opener = urllib.request.build_opener(NoRedirect)
for index, track in enumerate(job["tracks"], start=1):
    url = urljoin(BASE, track["audio_url"])
    if urlsplit(url).scheme != "https" or urlsplit(url).netloc != urlsplit(BASE).netloc:
        raise ValueError("Legacy external media URL: download it separately without your API key.")
    request = urllib.request.Request(url, headers={"Authorization": "Bearer " + KEY, "User-Agent": "UlazAI-Music-Example/1.0"})
    try:
        audio = opener.open(request, timeout=60)
    except urllib.error.HTTPError as error:
        if error.code not in {301, 302, 303, 307, 308} or not error.headers.get("Location"):
            raise
        # Storage URLs carry their own signature. Send no API key to storage.
        audio = urllib.request.urlopen(urllib.request.Request(urljoin(url, error.headers["Location"]), headers={"User-Agent": "UlazAI-Music-Example/1.0"}), timeout=120)
    with audio, Path(f"take-{index}.mp3").open("wb") as output:
        while chunk := audio.read(65536):
            output.write(chunk)
    print(f"Saved take-{index}.mp3", flush=True)
Node · built-in fetch

Requires Node 20 or later.

import { writeFile } from "node:fs/promises";
const BASE = "https://ulazai.com/api/v1/music/studio/";
const key = process.env.ULAZAI_API_KEY;
const requestId = process.env.ULAZAI_REQUEST_ID; // keep this UUID for retries
if (!key || !requestId) throw new Error("Set ULAZAI_API_KEY and ULAZAI_REQUEST_ID.");
const auth = { Authorization: `Bearer ${key}` };
async function call(path, payload) {
  const response = await fetch(new URL(path, BASE), {
    method: payload ? "POST" : "GET",
    headers: { ...auth, "Content-Type": "application/json" },
    body: payload ? JSON.stringify(payload) : undefined,
    signal: AbortSignal.timeout(60000),
  });
  const body = await response.json();
  if (!response.ok || !body.success) {
    throw new Error(`HTTP ${response.status}: ${body.error || body.detail || "API request failed"}`);
  }
  return body.data;
}
let job = await call("jobs/", {
  operation: "generate", request_id: requestId,
  custom_mode: true, instrumental: true,
  title: "Moonlit platform", style: "Gentle solo piano, warm analog texture",
  model: "V5_5", duration: 20,
});
console.log("Job:", job.id);
for (let i = 0; i < 120 && !["completed", "failed", "awaiting_user"].includes(job.status); i++) {
  await new Promise(resolve => setTimeout(resolve, 5000));
  job = await call(`jobs/${job.id}/`);
}
if (job.status !== "completed") {
  throw new Error(`Job ${job.id}: ${job.status}. ${job.error_message || ""} Resume with GET; keep the same request_id for submit retries.`);
}
for (const [index, track] of job.tracks.entries()) {
  const url = new URL(track.audio_url, BASE);
  if (url.origin !== new URL(BASE).origin) {
    throw new Error("Legacy external media URL: download it separately without your API key.");
  }
  let audio = await fetch(url, { headers: auth, redirect: "manual", signal: AbortSignal.timeout(60000) });
  if ([301, 302, 303, 307, 308].includes(audio.status)) {
    const location = audio.headers.get("Location");
    if (!location) throw new Error("Missing media redirect.");
    // Storage URLs carry their own signature. Send no API key to storage.
    audio = await fetch(new URL(location, url), { signal: AbortSignal.timeout(120000) });
  }
  if (!audio.ok) throw new Error(`Download HTTP ${audio.status}`);
  await writeFile(`take-${index + 1}.mp3`, Buffer.from(await audio.arrayBuffer()));
  console.log(`Saved take-${index + 1}.mp3`);
}

The scripts stop polling after 10 minutes on purpose. A track that still needs another minute is not lost: fetch the job again later and download when the status says completed.

Upload your own audio

Cover versions, extensions, vocal and instrumental additions and mashups start from your own file. Send it as multipart form data to the upload route, then reference it in the job. MP3, WAV, M4A, OGG and FLAC up to 50 MiB are accepted. Send the matching audio MIME type; the MP3 example explicitly sets audio/mpeg.

# Run in bash with curl and jq. Use an audio file you own.
curl --fail-with-body -sS "https://ulazai.com/api/v1/music/studio/upload/" \
  -H "Authorization: Bearer $ULAZAI_API_KEY" \
  -F "[email protected];type=audio/mpeg" > music-upload.json
UPLOAD_REFERENCE=$(jq -er '.data.upload_url' music-upload.json)

The response field is called upload_url, but it is a reference to your stored file, not a destination. Pass that value in the upload field of the job (and upload2 for the second file of a mashup):

# Create this UUID once for the cover, then keep it for retries.
# Use a different UUID from the quickstart's generation.
export ULAZAI_COVER_REQUEST_ID="$(python3 -c 'import uuid; print(uuid.uuid4())')"
payload=$(jq -n --arg id "$ULAZAI_COVER_REQUEST_ID" --arg upload "$UPLOAD_REFERENCE" \
  '{operation:"upload_cover",request_id:$id,upload:$upload,custom_mode:true,instrumental:true,title:"Warehouse bounce",style:"industrial techno, driving percussion",model:"V5_5"}')
curl --fail-with-body -sS "https://ulazai.com/api/v1/music/studio/jobs/" \
  -H "Authorization: Bearer $ULAZAI_API_KEY" \
  -H "Content-Type: application/json" --data "$payload"
# Read data.id and poll /jobs/<returned-id>/ as in the quickstart.

Rename a track or mark a favourite

PATCH changes the title and favourite flag of a job you own. The title is capped at 120 characters and cannot be empty; is_favorite takes a real boolean.

# JOB_ID is the data.id returned by your generation, saved in music-job.json.
JOB_ID=$(jq -er '.data.id' music-job.json)
curl --fail-with-body -sS -X PATCH "https://ulazai.com/api/v1/music/studio/jobs/$JOB_ID/" \
  -H "Authorization: Bearer $ULAZAI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"title":"Moonlit platform (final)","is_favorite":true}'

The job object

Submit and job-detail responses wrap one job in {"success": true, "data": {...}}. The library route wraps a list plus a cursor and returns 25 jobs per page; pass the next value, URL-encoded, as ?cursor= to keep reading. Songs from before the Music Studio appear in the library too, marked with is_legacy: true. Their audio URLs may point directly to an external host. Download those separately without your UlazAI API key; the authenticated generation examples reject external media URLs before sending any credentials. Legacy items are read-only and cannot serve as sources for new studio operations.

Fields of the data object returned by the job routes.
Field Meaning
id UUID of the job. Use it to poll and to download media.
music_id Internal provider task reference. Keep it for support questions; you never need it to call the API.
title Track title.
prompt The idea or lyrics the job was created with.
style Style description the job was created with.
instrumental true when the job requested a track without vocals.
model Model id, e.g. V5_5.
operation Operation id, e.g. generate.
status pending, processing, awaiting_user, completed or failed.
credits_used Credits charged. 0 for free operations.
created_at ISO-8601 timestamp in UTC.
error_message Sanitized error message, filled on failed jobs.
tracks Generated takes, usually two for generate. Each has id, title, audio_url, image_url, duration (seconds).
assets Extra outputs such as stems, WAV, MIDI, cover art or the music video. Each has label, url, type and sometimes id.
text Text output: lyrics, timestamped lyrics, boosted style, or the phrase to record for a custom voice.
is_favorite Your favourite flag, toggled with PATCH.
voice_id Persona or custom voice id when the job created one.
source_id The source job's UUID when this job built on an earlier track; otherwise null.

For new Music Studio jobs, media URLs in tracks and assets are relative API routes, stable for the life of the job. They answer authenticated requests with the file directly or a redirect to a signed URL that expires in minutes, which is why scripts fetch them fresh at download time instead of storing the links.

Five job states

Status values and what to do with each.
Status Meaning
pending Accepted and charged; submission to the provider has not been confirmed yet.
processing The provider is working. Keep polling.
awaiting_user Custom voice flow: the job's text holds the phrase to record. Continue with the voice operations below.
completed Result stored in private storage. Download via the media routes.
failed Something failed. Credits are refunded exactly once and error_message says what, as a sanitized message.

Refunds are automatic on failure. If a submit times out ambiguously, the charge stays in place while the server keeps verifying; if no result ever turns up, the job fails and the refund follows on its own. Keep the job ID while reconciliation is pending; a timeout is not proof that generation failed.

All 23 operations

Each operation below lists its field table straight from the live catalog, plus the rules the API actually enforces. R marks a required field.

generateextendupload_extendupload_coveradd_vocalsadd_instrumentalreplace_sectionmashupsoundsstemswavmidilyricstimestamped_lyricsstyle_boostcover_artmusic_videovoice_validatevoice_regeneratevoice_generatevoice_checkpersonarecovery

Create a song

Create a song generate 12 credits

Generate an original song from an idea or your own lyrics. Two takes are generated; both stay in your library.

Rules the API enforces

  • Idea mode (custom_mode off): prompt is required and capped at 3000 characters; model and instrumental still apply; title, style and detailed generation controls are not used.
  • Detailed controls on and instrumental on: style and title are required.
  • Detailed controls on and instrumental off: style, title and prompt are required.
  • duration is only read by V5_5 with custom_mode on. Leave it out otherwise. Valid range: 10-360 seconds.
  • persona_id needs custom_mode on and model V5 or V5_5, and must reference a persona or custom voice from your own library.
  • V4 caps style at 200 characters; the other models allow 1000.
Fields for generate. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "generate".
Field R Constraints Notes
prompt – string
max 5000 characters
A description of the song, or your own lyrics in custom mode.
custom_mode – boolean
true or false (JSON boolean)
defaults to off
On: set style, title and lyrics yourself. Off: describe the song and go.
title – string
max 80 characters
style – string
max 1000 characters
Genre and mood, e.g. 'upbeat pop, summer vibes'.
instrumental – boolean
true or false (JSON boolean)
defaults to on
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
duration – number
10 to 360
Only used by V5.5. Leave empty for automatic length.
negative_tags – string
max 500 characters
Styles to keep out, e.g. 'heavy metal'.
vocal_gender – string (enum)
one of: m, f
A preference, not a guarantee.
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).
persona_id – string Optional persona or custom voice from your library (V5/V5.5).
persona_model – string (enum)
one of: style_persona, voice_persona
Only for V5 and V5.5.

Continue existing material

Extend a track extend 12 credits

Continue one of your generated tracks from a chosen point.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • audio_id and continue_at are required. continue_at must land before the take ends.
  • default_param_flag defaults to true: pass new prompt, style and title. Set it false to inherit style and lyrics from the source take.
Fields for extend. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "extend".
Field R Constraints Notes
audio_id yes string Select the take to continue from its detail view.
continue_at yes number
at least 0
default_param_flag – boolean
true or false (JSON boolean)
defaults to on
Off: inherit style and lyrics from the original take.
prompt – string
max 5000 characters
title – string
max 80 characters
style – string
max 1000 characters
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
instrumental – boolean
true or false (JSON boolean)
negative_tags – string
max 500 characters
vocal_gender – string (enum)
one of: m, f
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).
Extend uploaded audio upload_extend 12 credits

Upload your own audio and continue it with generated material.

Needs 1 uploaded file via the upload field.

Rules the API enforces

  • upload and continue_at are required.
  • default_param_flag defaults to true: pass prompt, style and title. Set it false to keep the uploaded take's own material and just continue it.
Fields for upload_extend. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "upload_extend".
Field R Constraints Notes
upload yes string (upload reference) MP3 or WAV, up to 8 minutes.
continue_at yes number
at least 0
default_param_flag – boolean
true or false (JSON boolean)
defaults to on
prompt – string
max 5000 characters
title – string
max 80 characters
style – string
max 1000 characters
instrumental – boolean
true or false (JSON boolean)
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
negative_tags – string
max 500 characters
vocal_gender – string (enum)
one of: m, f
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).

Work from your own audio

Cover uploaded audio upload_cover 12 credits

Upload your own audio and turn it into a new production.

Needs 1 uploaded file via the upload field.

Rules the API enforces

  • upload is required: MP3 or WAV up to 8 minutes.
  • custom_mode defaults to true: style and title are required, plus prompt when instrumental is off. With custom_mode off, only prompt is needed.
Fields for upload_cover. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "upload_cover".
Field R Constraints Notes
upload yes string (upload reference) MP3 or WAV, up to 8 minutes.
custom_mode – boolean
true or false (JSON boolean)
defaults to on
instrumental – boolean
true or false (JSON boolean)
defaults to on
prompt – string
max 5000 characters
title – string
max 80 characters
style – string
max 1000 characters
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
negative_tags – string
max 500 characters
vocal_gender – string (enum)
one of: m, f
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).
Add vocals add_vocals 12 credits

Add a singing voice on top of your uploaded instrumental.

Needs 1 uploaded file via the upload field.

Rules the API enforces

  • upload, prompt (the lyrics), title and style are all required.
Fields for add_vocals. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "add_vocals".
Field R Constraints Notes
upload yes string (upload reference)
prompt yes string
max 5000 characters
title yes string
max 80 characters
style yes string
max 1000 characters
negative_tags – string
max 500 characters
vocal_gender – string (enum)
one of: m, f
model – string (enum)
one of the six model ids, see the model table
defaults to V5_5
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).
Add instrumental add_instrumental 12 credits

Generate an accompaniment under your uploaded vocal take.

Needs 1 uploaded file via the upload field.

Rules the API enforces

  • upload, title and tags are required.
Fields for add_instrumental. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "add_instrumental".
Field R Constraints Notes
upload yes string (upload reference)
title yes string
max 80 characters
tags yes string
max 1000 characters
Genre and mood for the accompaniment.
negative_tags – string
max 500 characters
model – string (enum)
one of the six model ids, see the model table
defaults to V5_5
vocal_gender – string (enum)
one of: m, f
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).
Replace a section replace_section 6 credits

Swap a chosen time range of a track for new material (infill).

Rules the API enforces

  • Provide exactly one input: source_id of a generated take, or upload. Not both.
  • With a source, audio_id selects the take.
  • prompt, tags, title, full_lyrics, infill_start_s and infill_end_s are required.
  • The section must be at least 10 seconds long, end within the track, and cover at most half of its duration.
Fields for replace_section. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "replace_section".
Field R Constraints Notes
source – string (job UUID) One of your generated takes, or upload audio below.
upload – string (upload reference) Use instead of a generated take.
audio_id – string Required when replacing from a generated track.
prompt yes string
max 5000 characters
tags yes string
max 1000 characters
title yes string
max 80 characters
infill_start_s yes number
at least 0
infill_end_s yes number
at least 0
full_lyrics yes string The complete lyrics including the new section.
negative_tags – string
max 500 characters
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
Mashup two tracks mashup 12 credits

Blend two uploaded tracks into one mashup.

Needs 2 uploaded files via the upload field.

Rules the API enforces

  • Both uploads, title and style are required. prompt is optional and capped at 500 characters.
Fields for mashup. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "mashup".
Field R Constraints Notes
upload yes string (upload reference)
upload2 yes string (upload reference)
title yes string
max 80 characters
style yes string
max 1000 characters
prompt – string
max 500 characters
instrumental – boolean
true or false (JSON boolean)
duration – number
10 to 360
model yes string (enum)
one of the six model ids, see the model table
defaults to V5_5
vocal_gender – string (enum)
one of: m, f
style_weight – number
0 to 1, step 0.01
How strictly to follow the style (0-1).
weirdness_constraint – number
0 to 1, step 0.01
How experimental the result may be (0-1).
audio_weight – number
0 to 1, step 0.01
Balance audio features against other input (0-1).

Sound design and formats

Generate a sound sounds 3 credits

Create loops, ambience and sound effects, optionally with BPM and key.

Rules the API enforces

  • prompt is required.
  • sound_tempo runs 20-300 BPM. sound_key is 8 characters max, e.g. D#m.
Fields for sounds. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "sounds".
Field R Constraints Notes
prompt yes string
max 5000 characters
model yes string (enum)
one of the six model ids, see the model table
defaults to V5
sound_loop – boolean
true or false (JSON boolean)
Suitable for seamless repeat playback.
sound_tempo – number
20 to 300
sound_key – string
max 8 characters
e.g. 'D#m'.
grab_lyrics – boolean
true or false (JSON boolean)
Separate stems stems from 10 credits

Split a track into vocals, instrumental or up to 12 instrument stems.

Rules the API enforces

  • Provide exactly one input: source_id of a generated take, or upload. Not both.
  • With a source, audio_id selects the take.
  • split_stem_advanced requires stem_name, e.g. Drums, Bass or Vocals.
Fields for stems. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "stems".
Field R Constraints Notes
source – string (job UUID) One of your generated takes, or upload audio below.
upload – string (upload reference)
audio_id – string
separation_type yes string (enum)
one of: separate_vocal, split_stem, split_stem_advanced
defaults to separate_vocal
stem_name – string Required for the single-stem mode, e.g. 'Drums', 'Bass', 'Vocals'.
Convert to WAV wav 1 credits

Download one of your tracks as a lossless WAV file.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • audio_id is required and must be one of the source job's takes.
Fields for wav. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "wav".
Field R Constraints Notes
audio_id yes string
Generate MIDI midi 0 credits

Turn a separated stem into editable MIDI note data.

Builds on an eligible job via source_id. Allowed source operations: stems (see the state requirements below).

Rules the API enforces

  • audio_id is optional. Pass a stem id from a stems result to convert exactly that stem.
Fields for midi. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "midi".
Field R Constraints Notes
audio_id – string Optional. Pick a specific stem from the separation result.

Lyrics and timing

Write lyrics lyrics 1 credits

Generate song lyrics from a theme or idea.

Rules the API enforces

  • prompt is required.
Fields for lyrics. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "lyrics".
Field R Constraints Notes
prompt yes string
max 5000 characters
Timestamped lyrics timestamped_lyrics 1 credits may finish inline

Get word-by-word timings for one of your takes (karaoke sync).

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • source_id and audio_id are required.
Fields for timestamped_lyrics. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "timestamped_lyrics".
Field R Constraints Notes
audio_id yes string
Boost a style description style_boost 1 credits may finish inline

Expand a short style idea into a rich, detailed style prompt.

Rules the API enforces

  • content is required, 500 characters max.
Fields for style_boost. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "style_boost".
Field R Constraints Notes
content yes string
max 500 characters
Short description, e.g. 'pop, mysterious'.

Artwork and video

Cover art cover_art 0 credits

Generate cover artwork for one of your takes.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • source_id and audio_id are required. The source must have been created with an original prompt.
Fields for cover_art. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "cover_art".
Field R Constraints Notes
audio_id yes string
Make a music video music_video 2 credits

Turn one of your tracks into a simple music video.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • source_id and audio_id are required.
Fields for music_video. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "music_video".
Field R Constraints Notes
audio_id yes string

Custom voices

Start custom voice voice_validate 0 credits

Create a custom singing voice from your own recording. You first get a phrase to read aloud and record.

Needs 1 uploaded file via the upload field.

Rules the API enforces

  • upload, vocal_start_s and vocal_end_s are required. The end must come after the start.
Fields for voice_validate. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "voice_validate".
Field R Constraints Notes
upload yes string (upload reference)
vocal_start_s yes number
at least 0
vocal_end_s yes number
at least 0
language – string
max 30 characters
New phrase for existing attempt voice_regenerate 0 credits

Ask for a fresh verification phrase; requires the earlier attempt as source.

Builds on an eligible job via source_id. Allowed source operations: voice_validate or voice_regenerate (see the state requirements below).

Rules the API enforces

  • source_id points at your voice_validate or voice_regenerate attempt in awaiting_user or completed state.
Fields for voice_regenerate. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "voice_regenerate".
Field R Constraints Notes
Finish custom voice voice_generate 0 credits

Submit your recording of the verification phrase and create the reusable voice.

Builds on an eligible job via source_id. Allowed source operations: voice_validate or voice_regenerate (see the state requirements below). Needs 1 uploaded file via the upload field or the operation's source.

Rules the API enforces

  • source_id and upload are required. The source voice_validate or voice_regenerate attempt must be awaiting_user or completed; upload your recording of its verification phrase.
Fields for voice_generate. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "voice_generate".
Field R Constraints Notes
upload yes string (upload reference)
voice_name – string
max 80 characters
style – string
max 1000 characters
singer_skill_level – string
max 40 characters
Check custom voice voice_check 0 credits may finish inline

Verify that a generated custom voice is ready to use.

Builds on an eligible job via source_id. Allowed source operations: voice_generate (see the state requirements below).

Rules the API enforces

  • source_id points at the voice_generate job.
Fields for voice_check. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "voice_check".
Field R Constraints Notes

Library helpers

Persona persona 0 credits may finish inline

Create a reusable persona (style or voice) from one of your takes.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • source_id and audio_id select the take. name and description are required.
Fields for persona. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "persona".
Field R Constraints Notes
audio_id yes string
name yes string
max 80 characters
description yes string
max 1000 characters
Recover lost audio recovery 0 credits

Regenerate fresh download links for an older generation whose links expired.

Builds on an eligible job via source_id. Allowed source operations: generate or extend or upload_cover or upload_extend or add_vocals or add_instrumental or replace_section or mashup or sounds (see the state requirements below).

Rules the API enforces

  • No fields. source_id points at the generation whose links expired.
Fields for recovery. All submissions go to POST https://ulazai.com/api/v1/music/studio/jobs/ with operation: "recovery".
Field R Constraints Notes

Field limits at a glance

Limits shared across operations. The catalog route is the source of truth.
Input Limit
style, tags1000 characters, 200 on V4
prompt5000 characters, 3000 in idea mode
title80 characters (120 for PATCH renames)
negative_tags500 characters
duration10 to 360 seconds, V5.5 with custom mode only
Style, weirdness and audio weights0 to 1, step 0.01
sound_tempo20 to 300 BPM
Replace-section spanAt least 10 seconds, at most half the track
Uploads50 MiB per file, MP3 / WAV / M4A / OGG / FLAC
Library pages25 jobs per page, cursor pagination
Free operations25 jobs per account per day, combined

Errors you will actually meet

Errors come back as {"success": false, "error": "...", "code": "..."}, often with the offending field. Authentication errors may instead use a detail field. For an ambiguous network or gateway error, retry the same request body and UUID; do not assume it was rejected or refunded.

Error codes, HTTP status and the fix.
Code HTTP What it means
invalid_request_id 400 request_id is missing or not a UUID.
unknown_operation 400 The operation id is not in the catalog.
invalid_field 400 A field failed validation. The error names the field.
invalid_source 400 source_id is missing, not yours, not completed, or not allowed for this operation.
invalid_audio_id 400 audio_id is not one of the source job's takes or assets.
invalid_persona 400 persona_id is not a persona or custom voice in your library.
invalid_upload 400 The upload reference is invalid, expired or not yours.
prompt_blocked 400 Prompt moderation rejected the submission. No credits were charged.
prompt_requires_review 400 Prompt moderation queued the text for review. No credits were charged.
request_id_conflict 409 This request_id was already used with a different body. Use a new UUID.
rate_limited 429 You hit the daily cap on free operations. Try again tomorrow.
insufficient_credits 402 The balance of purchased credits is too low. Welcome credits do not work over the API.
invalid_cursor 400 The library cursor is not a valid timestamp.
missing_file 400 The upload call had no file field.
unsupported_type 400 The upload is not an accepted audio type.
file_too_large 400 Uploads are capped at 50 MiB.
invalid_audio 400 The file does not contain playable audio.
401 response 401 The key is missing, malformed, revoked or wrong. Check the Authorization header.
404 response 404 No such job in your library. Job ids are UUIDs and library contents are private.
502 response 502 A definitive API submission failure is refunded. A proxy or network error can be ambiguous: retry the same body and request_id to recover the original job.

Is this Suno’s official API?

  • UlazAI is not affiliated with, endorsed by or sponsored by Suno. The name describes the music models this API exposes; the API itself is UlazAI's.
  • Check the UlazAI Terms of Use and the rights in any audio you upload before publishing or selling a result. API access alone is not a blanket rights clearance.
  • Generation time depends on model, duration and provider load. Treat several minutes as normal, poll with a bound, and never assume a fixed latency.
  • Model ids, prices and limits are current as of September 2026 and can change. The catalog route always reflects the live values; this page is generated from the same catalog.
  • Vocal gender is a preference, not a guarantee, and results vary between runs. Two takes per generate job means two chances, not a specified outcome.
Music Studio Songs, instrumentals and your own lyrics. AI song generator