curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain"
}'
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/music/generations",
data=json.dumps({
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response:
task = json.load(response)
print(task["id"], task["status"])
const response = await fetch("https://api.snapgen.org/v1/music/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "suno-music",
prompt: "A late-night city lo-fi song with soft piano and rain",
}),
});
const task = await response.json();
console.log(task.id, task.status);
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "queued",
"model": "suno-music",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "75000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "succeeded",
"model": "suno-music",
"modality": "audio",
"progress": 100,
"result": {
"urls": [
"https://cdn.example.com/music/rain-lofi-1.mp3",
"https://cdn.example.com/music/rain-lofi-2.mp3"
],
"tracks": [
{
"index": 1,
"id": "clip_8f3a2c",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-1.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-1.jpg",
"duration_seconds": 146.2,
"tags": "lo-fi, soft piano, rain"
},
{
"index": 2,
"id": "clip_9b4d1e",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-2.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-2.jpg",
"duration_seconds": 152.8,
"tags": "lo-fi, soft piano, rain"
}
]
},
"error": null,
"reserved_microusd": "75000",
"charged_microusd": "75000",
"created_at": 1790000000,
"completed_at": 1790000095
}
{
"error": {
"message": "source_task_id: source_task_id has not finished successfully",
"type": "invalid_request_error",
"param": null,
"code": "source_task_not_ready"
}
}
Endpoint Reference
Music generation tasks
Generate, edit, split, and analyze songs with Suno and Flow Music as asynchronous tasks, and chain operations by task ID.
POST
/
v1
/
music
/
generations
curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain"
}'
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/music/generations",
data=json.dumps({
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response:
task = json.load(response)
print(task["id"], task["status"])
const response = await fetch("https://api.snapgen.org/v1/music/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "suno-music",
prompt: "A late-night city lo-fi song with soft piano and rain",
}),
});
const task = await response.json();
console.log(task.id, task.status);
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "queued",
"model": "suno-music",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "75000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "succeeded",
"model": "suno-music",
"modality": "audio",
"progress": 100,
"result": {
"urls": [
"https://cdn.example.com/music/rain-lofi-1.mp3",
"https://cdn.example.com/music/rain-lofi-2.mp3"
],
"tracks": [
{
"index": 1,
"id": "clip_8f3a2c",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-1.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-1.jpg",
"duration_seconds": 146.2,
"tags": "lo-fi, soft piano, rain"
},
{
"index": 2,
"id": "clip_9b4d1e",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-2.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-2.jpg",
"duration_seconds": 152.8,
"tags": "lo-fi, soft piano, rain"
}
]
},
"error": null,
"reserved_microusd": "75000",
"charged_microusd": "75000",
"created_at": 1790000000,
"completed_at": 1790000095
}
{
"error": {
"message": "source_task_id: source_task_id has not finished successfully",
"type": "invalid_request_error",
"param": null,
"code": "source_task_not_ready"
}
}
POST /v1/music/generations runs every Suno and Flow Music model. Each model ID is one operation, such as generating a song, writing lyrics, extending a track, or splitting stems, and the body fields depend on that model. Every request becomes an asynchronous task: submit it, keep the task id, and read the result from GET /v1/tasks/{id}.
Follow-up operations take one of your earlier tasks as their source, so you can chain steps: generate a song, extend it, join the parts, then export or split it. Open a model’s page under Audio Series > Suno or Audio Series > Flow Music for its exact fields and an example.
Models
Suno
| Group | Model | What it does | Price |
|---|---|---|---|
| Create | suno-music | Generates songs with Suno V6, either from a description of the song or from your own lyrics. | $0.075 per request; $0.15 with max_mode: true |
| Create | suno-lyrics | Writes song lyrics about a theme you describe. | $0.012 per request |
| Create | suno-inspo | Generates a new song inspired by one to four public audio clips. | $0.102 per request; $0.204 with max_mode: true |
| Create | suno-sounds | Generates a sound effect from a description, as a single hit or a loop. | $0.0144 per request |
| Create | suno-style-boost | Expands short style tags into a richer style description that you can reuse in later requests. | $0.006 per request |
| Upload | suno-upload | Imports a public audio file as a Suno track, so later operations can use it as a source. | $0.006 per request |
| Upload | suno-upload-cover | Creates a cover of a public audio file in a new style, without a separate upload step. | $0.075 per request; $0.15 with max_mode: true |
| Upload | suno-upload-extend | Continues a public audio file from a chosen second, without a separate upload step. | $0.075 per request; $0.15 with max_mode: true |
| Upload | suno-custom-model | Trains a custom Suno model on 6 to 24 of your reference tracks. | $1.44 per request |
| Edit | suno-extend | Continues one of your Suno tracks from a chosen second, with new lyrics or a description. | $0.075 per request; $0.15 with max_mode: true |
| Edit | suno-cover | Covers one of your tracks in a different style. | $0.075 per request; $0.15 with max_mode: true |
| Edit | suno-remaster | Remasters one of your tracks to improve its quality, clarity, and overall texture. | $0.075 per request |
| Edit | suno-replace-section | Regenerates a time range of one of your tracks, optionally with new lyrics for that range. | $0.075 per request; $0.15 with max_mode: true |
| Edit | suno-remove-section | Removes a time range from one of your tracks. | $0.012 per request |
| Edit | suno-crop | Keeps only a time range of one of your tracks. | $0.012 per request |
| Edit | suno-fade-in | Adds a fade-in to the start of one of your tracks. | $0.012 per request |
| Edit | suno-fade-out | Adds a fade-out to the end of one of your tracks. | $0.012 per request |
| Edit | suno-speed | Changes the speed of one of your tracks, keeping its pitch unless you turn that off. | $0.036 per request |
| Edit | suno-concat | Joins an extension with the song it continues, producing one full-length track. | $0.006 per request |
| Edit | suno-mashup | Mixes two of your tracks into a new song. | $0.075 per request; $0.15 with max_mode: true |
| Edit | suno-sample | Builds a new song around a sampled time range of an uploaded track. | $0.075 per request; $0.15 with max_mode: true |
| Vocals & stems | suno-stems | Extracts one stem, such as the lead vocal or the drums, from one of your tracks. | $0.15 per request |
| Vocals & stems | suno-stems-all | Separates one of your tracks into all of its stems. | $0.36 per request |
| Vocals & stems | suno-add-vocals | Sings vocals over an instrumental you imported with suno-upload. | $0.075 per request; $0.15 with max_mode: true |
| Vocals & stems | suno-add-instrumental | Adds accompaniment to a vocal track you imported with suno-upload. | $0.075 per request; $0.15 with max_mode: true |
| Vocals & stems | suno-add-stem | Layers a new stem, such as strings or a synth part, onto one of your tracks. | $0.075 per request; $0.15 with max_mode: true |
| Vocals & stems | suno-voice | Creates a voice from a public MP3 or WAV recording. | $0.024 per request |
| Vocals & stems | suno-persona | Saves the singer of one of your tracks as a Persona. | $0.006 per request |
| Analysis & export | suno-midi | Transcribes one of your tracks into MIDI note data. | $0.075 per request |
| Analysis & export | suno-timed-lyrics | Returns the lyrics of one of your tracks with the start and end time of each entry. | $0.0012 per request |
| Analysis & export | suno-bpm | Measures the tempo of one of your tracks in beats per minute. | $0.0012 per request |
| Analysis & export | suno-mp4 | Renders a music video (MP4) for one of your tracks. | $0.006 per request |
| Analysis & export | suno-download | Exports one of your tracks as MP3, M4A, or WAV files, one file per requested format. | $0.0024 per request |
Flow Music
Flow Music runs Google Lyria. Each task returns one clip.| Model | What it does | Price |
|---|---|---|
flow-music | Generates one track from a style prompt, lyrics, or both with Flow Music (Google Lyria). | $0.09 per request |
flow-music-lyrics | Writes lyrics that you can pass to flow-music as lyrics. | $0.03 per request |
flow-music-extend | Continues one of your Flow Music clips from a chosen second, following an instruction. | $0.09 per request |
flow-music-replace | Regenerates a time range of one of your clips from an instruction. | $0.09 per request |
flow-music-cover | Rearranges one of your clips in a new style. | $0.09 per request |
flow-music-stems | Separates one of your clips into vocal and instrumental stems, delivered as one ZIP archive. | $0.09 per request |
flow-music-upload | Imports a public audio file as a Flow Music clip, so you can extend, replace, cover, split, download, or render it. | $0.015 per request |
flow-music-download | Exports one of your clips as an MP3 or WAV file. | $0.03 per request |
flow-music-video | Renders one of your clips into an MP4 video from a template. | $0.03 per request |
Request body
Send a JSON body withmodel, the fields that model defines, and optional
webhook fields. The body is validated strictly against the model’s contract: a
field the model doesn’t list returns invalid_music_request. These fields are
shared by several models:
string
required
A Suno or Flow Music model ID from the tables above.
string
Follow-up operations: the
id of your earlier task that holds the source
track, such as task_2f8KcX1mQpR7vT0yZa9Lb3. See
Reference earlier tasks.integer
default:"1"
The 1-based position of the source track in that task’s
result.tracks,
from 1 to 8.string[]
suno-mashup only: exactly two source task IDs.integer[]
suno-mashup only: the track position in each source task, in the same
order. Defaults to [1, 1].string
The
id of a succeeded suno-persona task, to sing with that Persona. Can’t
be combined with custom_model_task_id.string
The
id of a succeeded suno-custom-model task, to generate with that
model. Replaces version, so don’t send both.boolean
default:"false"
Suno Max mode, on the models whose price lists it. Bills twice the request
price and needs custom mode where the model has a
custom field.string
Optional public
https URL that receives one signed event when the task
reaches a final state. See Task webhooks.string
Required with
webhook: 16 to 256 characters, used to sign the delivery.string
Optional, 1 to 200 characters. Resending the same body with the same key
returns the original task instead of starting a second, billed one.
Suno modes
suno-music and the Suno models that write new music from a source, such as
suno-extend, suno-cover, and suno-mashup, have a custom field. Each
model page says whether it has one.
- Custom mode (
custom: true):promptholds your lyrics, and the style fields apply:styleonsuno-musicortagson the other models, plustitleandnegative_tags. - Description mode (
custom: false): the model writes the song from a description.suno-musicreads the description fromprompt; the other models read it fromgpt_description, which is then required.
custom field, max_mode and the target duration need
custom mode. max_mode: true bills twice the request price. variety is unrelated: variety: "max" doesn’t turn
on Max mode or change the price. Send version (v6, v6-wild, or
v6-mini; default v6) or custom_model_task_id, not both.
Submit a task
A valid request returns HTTP202 with a task object whose status is
queued. The Location header holds /v1/tasks/{id} and x-gateway-task-id
holds the task ID. The gateway reserves the request price at submission.
Read the result
PollGET /v1/tasks/{id} every 10 to 20 seconds until status is succeeded
or failed, or pass webhook to be notified instead. The task passes through
queued and processing; progress shows 0 to 100 when the provider reports
it. modality is audio.
When the task succeeds, result holds the normalized output. Which fields
appear depends on the model; each model page shows its result.
string[]
Every delivered media URL, primary output first. Empty for text-only results
such as lyrics or BPM.
object[]
Tracks in their original order. Follow-up operations select a track by its
index.Show track
Show track
integer
1-based position. Pass it as
source_index.string
The provider’s track or clip ID. For reference only: never send it back.
string
The audio file.
string
A WAV copy, when the provider returns one.
string
Cover art.
string
A video, when the operation renders one.
string
A non-audio file, such as a stems ZIP archive.
number
Length in seconds.
string
Track title.
string
Lyrics of the track.
string
Style tags.
string
The provider’s status for the track.
string
File format, such as
wav.string
Media type, such as
application/zip.number
File size.
object[]
suno-lyrics and flow-music-lyrics: entries with text and, when
returned, title and tags.object[]
suno-download: one format and url per requested format.string[]
suno-mp4 and flow-music-video: MP4 URLs.string
suno-style-boost: the expanded style tags.object
suno-persona: the Persona id. Reuse it through persona_task_id.object
suno-custom-model: the model id and name. Reuse it through
custom_model_task_id.object
suno-bpm: average, and min and max when reported.object[]
suno-timed-lyrics: entries with text, start_s, end_s, and success.object
suno-midi: state and instruments[], each with a name and notes[]
(pitch, start, end, velocity).object
suno-voice: the voice details the provider returns, as flat key-value
pairs.Result URLs are kept for about 72 hours. Download the files you want to keep
to your own storage. For convenience,
GET /v1/tasks/{id}/content redirects
to the first URL in result.urls.Reference earlier tasks
Follow-up operations never take provider IDs or file URLs of earlier results. You pass the SnapGen task ID of an earlier task, and the gateway resolves it:| Field | Points to |
|---|---|
source_task_id + source_index | One track of an earlier task. source_index counts from 1 in result.tracks and defaults to 1. |
source_task_ids + source_indexes | Two tracks, for suno-mashup. |
persona_task_id | A succeeded suno-persona task. |
custom_model_task_id | A succeeded suno-custom-model task. |
- Is yours. A task from another account is treated as missing:
source_task_not_found(HTTP404). - Has succeeded. A task that is still running, or that failed, returns
source_task_not_ready(HTTP409). Wait for a running task to succeed, then retry; a failed task can’t be a source. - Came from a compatible model. Each model page lists the models it
accepts as a source under Source tracks. For example,
suno-concattakes onlysuno-extendandsuno-upload-extendresults, andsuno-add-vocalstakes onlysuno-uploadresults. Otherwise:source_task_incompatible(HTTP400). - Has the track you picked. An index beyond the source’s tracks returns
source_index_out_of_range(HTTP400), with a message that says how many tracks the source has.
suno-upload or
flow-music-upload.
Workflow: generate, extend, and join
This example generates a song from lyrics, extends its first track from second 60, then joins the extension and the original into one full-length track. Each step waits for the previous task to succeed and passes its task ID forward.1
Generate the song
curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-music",
"custom": true,
"title": "Night Drive",
"style": "synthwave, female vocal",
"prompt": "[Verse]\nCity lights are calling me home\n[Chorus]\nWe keep on driving through the night"
}'
id, for example task_2f8KcX1mQpR7vT0yZa9Lb3, and poll
it until status is succeeded. Pick a track from result.tracks; its
index is the source_index for the next step.2
Extend a track
curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-extend",
"source_task_id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"source_index": 1,
"continue_at": 60,
"custom": true,
"prompt": "[Bridge]\nHeadlights fading into dawn"
}'
continue_at must be inside the source track. Poll the new task, for
example task_7hQmN2pX5rT8vW1yZc4Kd6, until it succeeds.3
Join the parts
curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-concat",
"source_task_id": "task_7hQmN2pX5rT8vW1yZc4Kd6",
"source_index": 1
}'
result.urls[0] is the full song. Download it
within about 72 hours, or export other formats with suno-download.import json
import os
import time
from urllib.request import Request, urlopen
API = "https://api.snapgen.org/v1"
AUTH = {"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}"}
FINAL = {"succeeded", "failed", "expired", "reconciliation_required"}
def submit(body):
request = Request(
f"{API}/music/generations",
data=json.dumps(body).encode(),
headers={**AUTH, "Content-Type": "application/json"},
)
with urlopen(request) as response:
return json.load(response)["id"]
def wait(task_id):
while True:
with urlopen(Request(f"{API}/tasks/{task_id}", headers=AUTH)) as response:
task = json.load(response)
if task["status"] in FINAL:
if task["status"] != "succeeded":
raise RuntimeError(f"{task_id} {task['status']}: {task['error']}")
return task["result"]
time.sleep(15)
song_id = submit({
"model": "suno-music",
"custom": True,
"title": "Night Drive",
"style": "synthwave, female vocal",
"prompt": "[Verse]\nCity lights are calling me home\n[Chorus]\nWe keep on driving through the night",
})
track = wait(song_id)["tracks"][0]
extension_id = submit({
"model": "suno-extend",
"source_task_id": song_id,
"source_index": track["index"],
"continue_at": 60, # a second inside the source track
"custom": True,
"prompt": "[Bridge]\nHeadlights fading into dawn",
})
wait(extension_id)
full_id = submit({"model": "suno-concat", "source_task_id": extension_id})
print("Full song:", wait(full_id)["urls"][0])
const API = "https://api.snapgen.org/v1";
const auth = { Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}` };
const FINAL = ["succeeded", "failed", "expired", "reconciliation_required"];
async function submit(body) {
const response = await fetch(`${API}/music/generations`, {
method: "POST",
headers: { ...auth, "Content-Type": "application/json" },
body: JSON.stringify(body),
});
if (!response.ok) throw new Error(await response.text());
return (await response.json()).id;
}
async function wait(taskId) {
for (;;) {
const task = await (await fetch(`${API}/tasks/${taskId}`, { headers: auth })).json();
if (FINAL.includes(task.status)) {
if (task.status !== "succeeded") {
throw new Error(`${taskId} ${task.status}: ${JSON.stringify(task.error)}`);
}
return task.result;
}
await new Promise((resolve) => setTimeout(resolve, 15_000));
}
}
const songId = await submit({
model: "suno-music",
custom: true,
title: "Night Drive",
style: "synthwave, female vocal",
prompt: "[Verse]\nCity lights are calling me home\n[Chorus]\nWe keep on driving through the night",
});
const [track] = (await wait(songId)).tracks;
const extensionId = await submit({
model: "suno-extend",
source_task_id: songId,
source_index: track.index,
continue_at: 60, // a second inside the source track
custom: true,
prompt: "[Bridge]\nHeadlights fading into dawn",
});
await wait(extensionId);
const fullId = await submit({ model: "suno-concat", source_task_id: extensionId });
console.log("Full song:", (await wait(fullId)).urls[0]);
Billing
Each music model bills a fixed price per request, listed in the tables above. Where the price lists a Max mode rate,max_mode: true bills twice the normal
price. The gateway reserves the price at submission and settles it when the
task succeeds. Failed tasks release the reservation and aren’t charged, and
polling is free. A chained workflow bills each step: the example above costs
$0.075 + $0.075 + $0.006 = $0.156.
Errors
| Status | error.code | Cause |
|---|---|---|
| 400 | invalid_music_request | A field is missing, unknown, or out of range, or a mode rule failed. The message names the field. |
| 400 | source_task_incompatible | A referenced task came from a model this operation doesn’t accept, or has no Persona, custom model, or clip to use. |
| 400 | source_index_out_of_range | source_index is beyond the tracks of the source task. |
| 400 | source_task_provider_mismatch | Referenced tasks ran on different provider accounts. |
| 400 | invalid_task_command | webhook or webhook_secret is missing or breaks the webhook rules. |
| 400 | invalid_idempotency_key | Idempotency-Key is empty or longer than 200 characters. |
| 402 | insufficient_funds | Your balance can’t cover the reservation. |
| 404 | model_not_available | The model ID is unknown or disabled. A key whose allowlist excludes the model gets api_key_model_not_allowed (403). |
| 404 | source_task_not_found | A referenced task doesn’t exist or isn’t yours. |
| 409 | source_task_not_ready | A referenced task hasn’t succeeded yet. |
| 409 | idempotency_conflict | The Idempotency-Key was already used with a different body. |
status: "failed" with error.code
provider_task_failed. See Errors for rate limits and other
failures.
curl https://api.snapgen.org/v1/music/generations \
-H "Authorization: Bearer $SNAPGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain"
}'
import json
import os
from urllib.request import Request, urlopen
request = Request(
"https://api.snapgen.org/v1/music/generations",
data=json.dumps({
"model": "suno-music",
"prompt": "A late-night city lo-fi song with soft piano and rain",
}).encode(),
headers={
"Authorization": f"Bearer {os.environ['SNAPGEN_API_KEY']}",
"Content-Type": "application/json",
},
)
with urlopen(request) as response:
task = json.load(response)
print(task["id"], task["status"])
const response = await fetch("https://api.snapgen.org/v1/music/generations", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.SNAPGEN_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: "suno-music",
prompt: "A late-night city lo-fi song with soft piano and rain",
}),
});
const task = await response.json();
console.log(task.id, task.status);
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "queued",
"model": "suno-music",
"modality": "audio",
"progress": null,
"result": null,
"error": null,
"reserved_microusd": "75000",
"charged_microusd": null,
"created_at": 1790000000,
"completed_at": null
}
{
"id": "task_2f8KcX1mQpR7vT0yZa9Lb3",
"object": "task",
"status": "succeeded",
"model": "suno-music",
"modality": "audio",
"progress": 100,
"result": {
"urls": [
"https://cdn.example.com/music/rain-lofi-1.mp3",
"https://cdn.example.com/music/rain-lofi-2.mp3"
],
"tracks": [
{
"index": 1,
"id": "clip_8f3a2c",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-1.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-1.jpg",
"duration_seconds": 146.2,
"tags": "lo-fi, soft piano, rain"
},
{
"index": 2,
"id": "clip_9b4d1e",
"title": "Rain on the Window",
"audio_url": "https://cdn.example.com/music/rain-lofi-2.mp3",
"image_url": "https://cdn.example.com/music/rain-lofi-2.jpg",
"duration_seconds": 152.8,
"tags": "lo-fi, soft piano, rain"
}
]
},
"error": null,
"reserved_microusd": "75000",
"charged_microusd": "75000",
"created_at": 1790000000,
"completed_at": 1790000095
}
{
"error": {
"message": "source_task_id: source_task_id has not finished successfully",
"type": "invalid_request_error",
"param": null,
"code": "source_task_not_ready"
}
}