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

# Quickstart

> Make your first API call in under 10 minutes

import { Steps, Step, Tabs, Tab, CodeGroup, Note, Tip, Warning } from 'mintlify/components'

## Overview

Every Soundpiece operation follows the same pattern:

1. **Submit** a request — receive an operation `id` and `status: processing`.
2. **Poll** the same endpoint (or wait for a webhook) until `status` is `ready` or `failed`.
3. **Download** the audio using the signed URL in the response.

This guide walks through a complete example: generating a song from lyrics and a prompt.

***

## Step 1: Get your API key

API keys are issued from your account dashboard. Each key comes paired with a **webhook signing secret**. Both are shown **once at creation** — save them somewhere safe.

Keys start with the prefix `spk_` — the prefix is part of the key, so use the value exactly as the dashboard shows it. Pass yours as a Bearer token on every request:

```http theme={null}
Authorization: Bearer <your-api-key>
```

<Warning>
  Anyone with your key can use your account. Never embed it in client-side or mobile application code. If a key is exposed, revoke it from the dashboard and create a new one.
</Warning>

***

## Step 2: Submit a song generation request

Pass an `idempotency_key` (a UUID you choose) so you can safely retry the request over an unreliable network.

<CodeGroup>
  ```bash cURL theme={null}
  status=$(curl --silent --output response.json --write-out '%{http_code}' \
    --request PUT \
    --url https://api.soundpiece.co/v1/create.new_song \
    --header 'Authorization: Bearer <your-api-key>' \
    --header 'Content-Type: application/json' \
    --data '{
      "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
      "lyrics": "I've been driving all night, my hands wet on the wheel",
      "prompt": "Synthwave, melancholic, mid-tempo"
    }')

  if [ "$status" -ne 200 ]; then
    echo "Error (HTTP $status):"
  else
    echo "Success:"
  fi
  jq . response.json
  ```

  ```python Python theme={null}
  import json
  import uuid
  import requests

  response = requests.put(
      "https://api.soundpiece.co/v1/create.new_song",
      headers={"Authorization": "Bearer <your-api-key>"},
      json={
          "idempotency_key": str(uuid.uuid4()),
          "lyrics": "I've been driving all night, my hands wet on the wheel",
          "prompt": "Synthwave, melancholic, mid-tempo",
      },
  )

  body = response.json()
  if response.status_code != 200:
      print(f"Error (HTTP {response.status_code}):")
      print(json.dumps(body, indent=2))
  else:
      print("Success:")
      print(json.dumps(body, indent=2))
      song = body["song"]
      # ...continue to Step 3 with song["id"]
  ```

  ```javascript Node.js theme={null}
  import { randomUUID } from "node:crypto";

  const response = await fetch("https://api.soundpiece.co/v1/create.new_song", {
    method: "PUT",
    headers: {
      Authorization: "Bearer <your-api-key>",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      idempotency_key: randomUUID(),
      lyrics: "I've been driving all night, my hands wet on the wheel",
      prompt: "Synthwave, melancholic, mid-tempo",
    }),
  });

  const body = await response.json();
  if (!response.ok) {
    console.error(`Error (HTTP ${response.status}):`);
    console.error(JSON.stringify(body, null, 2));
  } else {
    console.log("Success:");
    console.log(JSON.stringify(body, null, 2));
    const { song } = body;
    // ...continue to Step 3 with song.id
  }
  ```
</CodeGroup>

**Response**

```json theme={null}
{
  "song": {
    "id": "op_01h7k...",
    "status": "processing",
    "created_at": "2026-05-20T12:34:56Z",
    "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
    "lyrics": "I've been driving all night, my hands wet on the wheel",
    "prompt": "Synthwave, melancholic, mid-tempo",
    "reference": null,
    "outputs": null,
    "error": null
  }
}
```

The `id` (operation id) is the handle you'll use to fetch the result. Notice that the request you sent is echoed back on the response — useful for keeping the request and its outcome together without your own bookkeeping.

***

## Step 3: Poll until the operation is ready

Call the same endpoint with `GET` and the operation `id`. Pass `?wait=true` to long-poll for up to 20 seconds — the server returns as soon as the operation reaches `ready` or `failed`, or after 20 seconds if it is still running. Repeat until you get a terminal status.

<CodeGroup>
  ```bash cURL theme={null}
  status=$(curl --silent --output response.json --write-out '%{http_code}' \
    --request GET \
    --url 'https://api.soundpiece.co/v1/create.new_song?id=op_01h7k...&wait=true' \
    --header 'Authorization: Bearer <your-api-key>')

  if [ "$status" -ne 200 ]; then
    echo "Error (HTTP $status):"
  else
    echo "Success:"
  fi
  jq . response.json
  ```

  ```python Python theme={null}
  import time

  def wait_for_song(operation_id, api_key, timeout=300):
      start = time.time()
      while time.time() - start < timeout:
          response = requests.get(
              "https://api.soundpiece.co/v1/create.new_song",
              headers={"Authorization": f"Bearer {api_key}"},
              params={"id": operation_id, "wait": "true"},
          )
          if response.status_code != 200:
              print(f"Error (HTTP {response.status_code}):")
              print(json.dumps(response.json(), indent=2))
              return None
          song = response.json()["song"]
          if song["status"] in ("ready", "failed"):
              return song
      raise TimeoutError("Operation did not complete in time")

  result = wait_for_song(song["id"], "<your-api-key>")
  if result is not None:
      print("Success:" if result["status"] == "ready" else "Error:")
      print(json.dumps(result, indent=2))
  ```

  ```javascript Node.js theme={null}
  async function waitForSong(operationId, apiKey, timeoutMs = 300000) {
    const start = Date.now();
    while (Date.now() - start < timeoutMs) {
      const res = await fetch(
        `https://api.soundpiece.co/v1/create.new_song?id=${operationId}&wait=true`,
        { headers: { Authorization: `Bearer ${apiKey}` } }
      );
      if (!res.ok) {
        console.error(`Error (HTTP ${res.status}):`);
        console.error(JSON.stringify(await res.json(), null, 2));
        return null;
      }
      const { song } = await res.json();
      if (song.status === "ready" || song.status === "failed") return song;
    }
    throw new Error("Operation timed out");
  }

  const result = await waitForSong(song.id, "<your-api-key>");
  if (result !== null) {
    console.log(result.status === "ready" ? "Success:" : "Error:");
    console.log(JSON.stringify(result, null, 2));
  }
  ```
</CodeGroup>

**Response when ready**

```json theme={null}
{
  "song": {
    "id": "op_01h7k...",
    "status": "ready",
    "created_at": "2026-05-20T12:34:56Z",
    "idempotency_key": "550e8400-e29b-41d4-a716-446655440000",
    "lyrics": "I've been driving all night, my hands wet on the wheel",
    "prompt": "Synthwave, melancholic, mid-tempo",
    "reference": null,
    "outputs": [
      {
        "url": "https://cdn.soundpiece.co/...signed...",
        "expires_at": "2026-05-20T12:50:21Z",
        "duration": 87.4
      },
      {
        "url": "https://cdn.soundpiece.co/...signed...",
        "expires_at": "2026-05-20T12:50:21Z",
        "duration": 91.2
      }
    ],
    "error": null
  }
}
```

Song generation typically produces multiple variations — each appears as a separate entry in `outputs`.

<Tip>
  For production workloads, use [webhooks](/webhooks) so you don't have to poll. Supply a `callback_url` on the original `PUT` and we'll post the same response to that URL when the operation terminates. Polling remains the canonical source of truth — see [Async operations](/async-operations).
</Tip>

***

## Step 4: Download the audio

Each output carries a **signed URL** you can `GET` to download the audio as a FLAC file (44.1 kHz). The URL is time-limited; check the `expires_at` field. If it has expired, call the `GET` endpoint again — we'll generate a fresh URL.

<CodeGroup>
  ```bash cURL theme={null}
  curl --output song.flac \
    'https://cdn.soundpiece.co/...signed...'
  ```

  ```python Python theme={null}
  with requests.get(result["outputs"][0]["url"], stream=True) as r:
      r.raise_for_status()
      with open("song.flac", "wb") as f:
          for chunk in r.iter_content(chunk_size=64 * 1024):
              f.write(chunk)
  print("Saved to song.flac")
  ```

  ```javascript Node.js theme={null}
  import fs from "node:fs";

  const audio = await fetch(result.outputs[0].url);
  const buffer = await audio.arrayBuffer();
  fs.writeFileSync("song.flac", Buffer.from(buffer));
  console.log("Saved to song.flac");
  ```
</CodeGroup>

No auth header is needed on the download — the signed URL is self-authenticating until it expires.

***

That's it. The same pattern applies to every endpoint:

* `PUT /v1/create.new_song` — generate a song from lyrics + prompt or reference.
* `PUT /v1/create.new_instrumental` — generate an instrumental from a prompt or reference.
* `PUT /v1/create.new_sample` — generate a short sample at a target duration.
* `PUT /v1/create.new_fx` — generate a one-shot effect.
* `PUT /v1/create.remix_song` — remix an existing song (with vocals/lyrics) into new song variations, guided by `lyrics` and `prompt`.
* `PUT /v1/create.remix_instrumental` — remix an existing instrumental track into new instrumental variations.
* `PUT /v1/adapt.separate_stems` — separate a track into vocals, drums, bass, other.
* `PUT /v1/adapt.auto_cut_down` — trim a track to a target duration at musically natural cut points.

See the [API Reference](/api-reference) for the full surface, or read on for [authentication](/authentication), [async operations](/async-operations), [webhooks](/webhooks), [errors](/errors), [rate limits](/rate-limits), and [versioning](/versioning).
