Skip to main content
Text-to-Music is asynchronous and poll-based. It does not need a cloned voice, a workspace session or a callback URL. Every request uses the accessKey header, and create/accept endpoints also require an Idempotency-Key.
Runnable clients for this guide: examples/music/quickstart.py and examples/music/quickstart.mjs. Each one implements the whole flow below, including the polling and the error handling.

Endpoint summary

Step 1: Read capabilities

capabilities tells you the account state, the rate card and the accepted enum values. Read the genre, mood, style, duration and vocal-language ranges from this response instead of hard-coding them.
Relevant fields:
  • accessState and planKey: whether this account may generate music.
  • ratePerMinute: the Characters rate for the account’s plan. The charge is ceil(durationSec × ratePerMinute / 60).
  • supportedDurationsSec: the durations you may request (60, 90, 120, 180).
  • supportedVocalLanguages: an array of {code, name} entries. Send the code (for example en), not the display name.
  • genres, moods, vocalStyles, lyricsModes: the accepted enum values.
  • quoteTtlSeconds: how long a quote stays valid (600).

Step 2: Create the project

A successful response returns the project and the arrangement job:
Idempotency-Key rules:
  • Must be unique per distinct operation, 16–64 printable ASCII characters.
  • The same key with the same body replays safely; the same key with a different body is rejected with code = 47008.
  • A replay does not return the first response byte-for-byte. Keep the projectId/jobId and re-query the resource instead. See Async jobs, polling and retries.

Step 3: Poll until the arrangement is ready

Poll GET /projects/{projectId} (or the job with GET /jobs/{jobId}) until one of these is true:
  • projectStatus is ARRANGEMENT_READY: the arrangement is finished and you may quote.
  • the job’s terminal field is true: stop. Inspect lastFailure (project) or failure (job).
ARRANGEMENT_READY means the arrangement is done — not that the song exists. The finished song is only available when projectStatus is READY and readyAsset is present.

Step 4: Review and optionally edit the arrangement

GET /projects/{projectId} returns arrangement plus arrangementVersion. To change it:
You send only the fields that may change, addressed by sectionId. The service merges them into the immutable arrangement and re-validates the result. If expectedVersion no longer matches, the request is rejected with code = 47016; re-read the project and retry. To ask the service for a fresh arrangement instead:
Editing or regenerating the arrangement invalidates the current quote.

Step 5: Quote before you spend Characters

The quote tells you exactly what the generation will cost:
Every Characters value in this response (quotedCharacters, shortfall, remainingAfterGeneration, balances.*) is a JSON string, because the API serializes 64-bit integers as strings to preserve precision. Convert it to a real integer before doing arithmetic or comparisons — never compare these values as strings. Quotes expire after quoteTtlSeconds (10 minutes); an expired quote is rejected with code = 47006.

Step 6: Generate the song

reservedCharacters is a string for the same reason as above. Generation does not wait for the song; poll the job or the project.
A quote can be consumed only once. Submitting the same quoteId again with a different idempotency key is rejected with code = 47007. To recover, keep the projectId/jobId and query the existing project instead of re-submitting.

Step 7: Poll the song

Poll GET /projects/{projectId} until projectStatus is READY and readyAsset is present, or until the job is terminal with a failure. Typical progress: For jobs, job.status is QUEUED, GENERATING, FINALIZING, SETTLING, READY, FAILED or RECOVERY_REQUIRED, and terminal is true for READY, FAILED and RELEASED. displayStage gives a coarser, user-facing stage (QUEUED, CREATING_MUSIC_AND_VOCALS, FINALIZING_LIBRARY_ITEM, RECOVERY_REQUIRED). RECOVERY_REQUIRED is not a failure and not a refund. Keep the projectId/jobId and query them; do not submit a new paid generation.

Step 8: Play and download

Returns { "assetId": "...", "url": "...", "expiresAt": "..." }. The playback URL is temporary (10 minutes), so request it when you need it rather than storing it.
GET /download streams audio/mpeg bytes — it is not a JSON envelope on success. But when the request is rejected, the service answers with a JSON error envelope instead. Check the response Content-Type (or the code in the body) before saving the file, otherwise you may write an error message into a .mp3.

Managing projects

  • GET /sound_clone/api/v1/music/projects?page=1&pageSize=20 lists your projects. page must be ≥ 1 and pageSize must be 10, 20 or 50.
  • PATCH /sound_clone/api/v1/music/projects/{projectId}/name with { "name": "..." } renames a project.
  • DELETE /sound_clone/api/v1/music/projects/{projectId} deletes it. If a generation is still in flight the request is rejected with code = 47017 and the job is not cancelled. A successful delete is scoped to your ownership and is idempotent: success means the call needs no further delete handling — it is not proof that the project existed or that it was removed by this call.

Billing in one sentence

Quoting is free; generating reserves Characters at ceil(durationSec × ratePerMinute / 60), and the shared ledger settles or releases the reservation according to the real outcome. Full detail is in Characters, quotes and billing.