> ## Documentation Index
> Fetch the complete documentation index at: https://docs.snapgen.org/llms.txt
> Use this file to discover all available pages before exploring further.

# 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` 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}`](/api-reference/task-status).

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`](/api-manual/audio/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`](/api-manual/audio/suno-lyrics) | Writes song lyrics about a theme you describe. | \$0.012 per request |
| Create | [`suno-inspo`](/api-manual/audio/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`](/api-manual/audio/suno-sounds) | Generates a sound effect from a description, as a single hit or a loop. | \$0.0144 per request |
| Create | [`suno-style-boost`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/suno-custom-model) | Trains a custom Suno model on 6 to 24 of your reference tracks. | \$1.44 per request |
| Edit | [`suno-extend`](/api-manual/audio/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`](/api-manual/audio/suno-cover) | Covers one of your tracks in a different style. | \$0.075 per request; \$0.15 with `max_mode: true` |
| Edit | [`suno-remaster`](/api-manual/audio/suno-remaster) | Remasters one of your tracks to improve its quality, clarity, and overall texture. | \$0.075 per request |
| Edit | [`suno-replace-section`](/api-manual/audio/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`](/api-manual/audio/suno-remove-section) | Removes a time range from one of your tracks. | \$0.012 per request |
| Edit | [`suno-crop`](/api-manual/audio/suno-crop) | Keeps only a time range of one of your tracks. | \$0.012 per request |
| Edit | [`suno-fade-in`](/api-manual/audio/suno-fade-in) | Adds a fade-in to the start of one of your tracks. | \$0.012 per request |
| Edit | [`suno-fade-out`](/api-manual/audio/suno-fade-out) | Adds a fade-out to the end of one of your tracks. | \$0.012 per request |
| Edit | [`suno-speed`](/api-manual/audio/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`](/api-manual/audio/suno-concat) | Joins an extension with the song it continues, producing one full-length track. | \$0.006 per request |
| Edit | [`suno-mashup`](/api-manual/audio/suno-mashup) | Mixes two of your tracks into a new song. | \$0.075 per request; \$0.15 with `max_mode: true` |
| Edit | [`suno-sample`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/suno-stems-all) | Separates one of your tracks into all of its stems. | \$0.36 per request |
| Vocals & stems | [`suno-add-vocals`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/suno-voice) | Creates a voice from a public MP3 or WAV recording. | \$0.024 per request |
| Vocals & stems | [`suno-persona`](/api-manual/audio/suno-persona) | Saves the singer of one of your tracks as a Persona. | \$0.006 per request |
| Analysis & export | [`suno-midi`](/api-manual/audio/suno-midi) | Transcribes one of your tracks into MIDI note data. | \$0.075 per request |
| Analysis & export | [`suno-timed-lyrics`](/api-manual/audio/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`](/api-manual/audio/suno-bpm) | Measures the tempo of one of your tracks in beats per minute. | \$0.0012 per request |
| Analysis & export | [`suno-mp4`](/api-manual/audio/suno-mp4) | Renders a music video (MP4) for one of your tracks. | \$0.006 per request |
| Analysis & export | [`suno-download`](/api-manual/audio/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`](/api-manual/audio/flow-music) | Generates one track from a style prompt, lyrics, or both with Flow Music (Google Lyria). | \$0.09 per request |
| [`flow-music-lyrics`](/api-manual/audio/flow-music-lyrics) | Writes lyrics that you can pass to `flow-music` as `lyrics`. | \$0.03 per request |
| [`flow-music-extend`](/api-manual/audio/flow-music-extend) | Continues one of your Flow Music clips from a chosen second, following an instruction. | \$0.09 per request |
| [`flow-music-replace`](/api-manual/audio/flow-music-replace) | Regenerates a time range of one of your clips from an instruction. | \$0.09 per request |
| [`flow-music-cover`](/api-manual/audio/flow-music-cover) | Rearranges one of your clips in a new style. | \$0.09 per request |
| [`flow-music-stems`](/api-manual/audio/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`](/api-manual/audio/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`](/api-manual/audio/flow-music-download) | Exports one of your clips as an MP3 or WAV file. | \$0.03 per request |
| [`flow-music-video`](/api-manual/audio/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 with `model`, 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:

<ParamField body="model" type="string" required>
  A Suno or Flow Music model ID from the tables above.
</ParamField>

<ParamField body="source_task_id" type="string">
  Follow-up operations: the `id` of your earlier task that holds the source
  track, such as `task_2f8KcX1mQpR7vT0yZa9Lb3`. See
  [Reference earlier tasks](#reference-earlier-tasks).
</ParamField>

<ParamField body="source_index" type="integer" default="1">
  The 1-based position of the source track in that task's `result.tracks`,
  from `1` to `8`.
</ParamField>

<ParamField body="source_task_ids" type="string[]">
  `suno-mashup` only: exactly two source task IDs.
</ParamField>

<ParamField body="source_indexes" type="integer[]">
  `suno-mashup` only: the track position in each source task, in the same
  order. Defaults to `[1, 1]`.
</ParamField>

<ParamField body="persona_task_id" type="string">
  The `id` of a succeeded `suno-persona` task, to sing with that Persona. Can't
  be combined with `custom_model_task_id`.
</ParamField>

<ParamField body="custom_model_task_id" type="string">
  The `id` of a succeeded `suno-custom-model` task, to generate with that
  model. Replaces `version`, so don't send both.
</ParamField>

<ParamField body="max_mode" type="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.
</ParamField>

<ParamField body="webhook" type="string">
  Optional public `https` URL that receives one signed event when the task
  reaches a final state. See [Task webhooks](/api-reference/webhooks).
</ParamField>

<ParamField body="webhook_secret" type="string">
  Required with `webhook`: 16 to 256 characters, used to sign the delivery.
</ParamField>

<ParamField header="Idempotency-Key" type="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.
</ParamField>

## 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`): `prompt` holds your lyrics, and the style
  fields apply: `style` on `suno-music` or `tags` on the other models, plus
  `title` and `negative_tags`.
* **Description mode** (`custom: false`): the model writes the song from a
  description. `suno-music` reads the description from `prompt`; the other
  models read it from `gpt_description`, which is then required.

On models with a `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 HTTP `202` 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

Poll `GET /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.

<ResponseField name="result.urls" type="string[]">
  Every delivered media URL, primary output first. Empty for text-only results
  such as lyrics or BPM.
</ResponseField>

<ResponseField name="result.tracks" type="object[]">
  Tracks in their original order. Follow-up operations select a track by its
  `index`.

  <Expandable title="track">
    <ResponseField name="index" type="integer">
      1-based position. Pass it as `source_index`.
    </ResponseField>

    <ResponseField name="id" type="string">
      The provider's track or clip ID. For reference only: never send it back.
    </ResponseField>

    <ResponseField name="audio_url" type="string">
      The audio file.
    </ResponseField>

    <ResponseField name="wav_url" type="string">
      A WAV copy, when the provider returns one.
    </ResponseField>

    <ResponseField name="image_url" type="string">
      Cover art.
    </ResponseField>

    <ResponseField name="video_url" type="string">
      A video, when the operation renders one.
    </ResponseField>

    <ResponseField name="file_url" type="string">
      A non-audio file, such as a stems ZIP archive.
    </ResponseField>

    <ResponseField name="duration_seconds" type="number">
      Length in seconds.
    </ResponseField>

    <ResponseField name="title" type="string">
      Track title.
    </ResponseField>

    <ResponseField name="lyrics" type="string">
      Lyrics of the track.
    </ResponseField>

    <ResponseField name="tags" type="string">
      Style tags.
    </ResponseField>

    <ResponseField name="status" type="string">
      The provider's status for the track.
    </ResponseField>

    <ResponseField name="format" type="string">
      File format, such as `wav`.
    </ResponseField>

    <ResponseField name="mime_type" type="string">
      Media type, such as `application/zip`.
    </ResponseField>

    <ResponseField name="size_bytes" type="number">
      File size.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="result.lyrics" type="object[]">
  `suno-lyrics` and `flow-music-lyrics`: entries with `text` and, when
  returned, `title` and `tags`.
</ResponseField>

<ResponseField name="result.files" type="object[]">
  `suno-download`: one `format` and `url` per requested format.
</ResponseField>

<ResponseField name="result.videos" type="string[]">
  `suno-mp4` and `flow-music-video`: MP4 URLs.
</ResponseField>

<ResponseField name="result.text" type="string">
  `suno-style-boost`: the expanded style tags.
</ResponseField>

<ResponseField name="result.persona" type="object">
  `suno-persona`: the Persona `id`. Reuse it through `persona_task_id`.
</ResponseField>

<ResponseField name="result.custom_model" type="object">
  `suno-custom-model`: the model `id` and `name`. Reuse it through
  `custom_model_task_id`.
</ResponseField>

<ResponseField name="result.bpm" type="object">
  `suno-bpm`: `average`, and `min` and `max` when reported.
</ResponseField>

<ResponseField name="result.timed_lyrics" type="object[]">
  `suno-timed-lyrics`: entries with `text`, `start_s`, `end_s`, and `success`.
</ResponseField>

<ResponseField name="result.midi" type="object">
  `suno-midi`: `state` and `instruments[]`, each with a `name` and `notes[]`
  (`pitch`, `start`, `end`, `velocity`).
</ResponseField>

<ResponseField name="result.voice" type="object">
  `suno-voice`: the voice details the provider returns, as flat key-value
  pairs.
</ResponseField>

<Warning>
  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`.
</Warning>

## 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. |

Before it reserves any balance, the gateway checks that each referenced task:

1. **Is yours.** A task from another account is treated as missing:
   `source_task_not_found` (HTTP `404`).
2. **Has succeeded.** A task that is still running, or that failed, returns
   `source_task_not_ready` (HTTP `409`). Wait for a running task to succeed,
   then retry; a failed task can't be a source.
3. **Came from a compatible model.** Each model page lists the models it
   accepts as a source under **Source tracks**. For example, `suno-concat`
   takes only `suno-extend` and `suno-upload-extend` results, and
   `suno-add-vocals` takes only `suno-upload` results. Otherwise:
   `source_task_incompatible` (HTTP `400`).
4. **Has the track you picked.** An index beyond the source's tracks returns
   `source_index_out_of_range` (HTTP `400`), with a message that says how many
   tracks the source has.

The follow-up then runs on the same provider account as its source, and the
gateway substitutes the provider's own task and track IDs for you. Suno models
reference Suno tasks, and Flow Music models reference Flow Music tasks. To use
your own audio as a source, import it first with
[`suno-upload`](/api-manual/audio/suno-upload) or
[`flow-music-upload`](/api-manual/audio/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.

<Steps>
  <Step title="Generate the song">
    ```bash theme={null}
    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"
      }'
    ```

    Save the returned `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.
  </Step>

  <Step title="Extend a track">
    ```bash theme={null}
    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.
  </Step>

  <Step title="Join the parts">
    ```bash theme={null}
    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
      }'
    ```

    When this task succeeds, `result.urls[0]` is the full song. Download it
    within about 72 hours, or export other formats with `suno-download`.
  </Step>
</Steps>

The same chain as a script:

<CodeGroup>
  ```python Python theme={null}
  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])
  ```

  ```javascript Node.js theme={null}
  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]);
  ```
</CodeGroup>

## 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](/api-reference/webhooks). |
| 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. |

A task that fails at the provider reports `status: "failed"` with `error.code`
`provider_task_failed`. See [Errors](/errors) for rate limits and other
failures.

<RequestExample>
  ```bash Curl theme={null}
  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"
    }'
  ```

  ```python Python theme={null}
  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"])
  ```

  ```javascript Node.js theme={null}
  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);
  ```
</RequestExample>

<ResponseExample>
  ```json 202 Queued theme={null}
  {
    "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
  }
  ```

  ```json Succeeded theme={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
  }
  ```

  ```json 409 theme={null}
  {
    "error": {
      "message": "source_task_id: source_task_id has not finished successfully",
      "type": "invalid_request_error",
      "param": null,
      "code": "source_task_not_ready"
    }
  }
  ```
</ResponseExample>
