Skip to content

Music generations

Songs from a prompt and lyrics, the wait for a result, the music time budget, and the model list.

Access: Keys with the music:read or music:generate scope, on an account with music access granted by Albastar Corp.

A music generation makes one song from a prompt, with lyrics or as an instrumental, and keeps the song in the account's library at https://music.albastov.com. POST /v1/music/generations and DELETE /v1/music/generations/{id} need the music:generate scope, which includes music:read; the other generation routes need music:read. GET /v1/music/models needs no key.

Errors on these routes have the shape {"ok": false, "error": "bad-param", "code": "bad-param", "message": "...", "param": "prompt"}; only bad-param carries param. Branch on code. Key, scope and rate-limit errors: Errors.

The generation object

FieldTypeValue
idstringj-, the UTC date as YYMMDD, - and 6 random characters, such as j-261104-k3v9qa
objectstringmusic.generation
modelstringThe model id, ace or yue2
statestringqueued, running, done, failed or cancelled
paramsobjectThe parameters as the generation runs them: prompt, lyrics, instrumental, duration_s (null when the model picks the length), seed, format and title
positioninteger or nullPlace in the queue while queued, from 1
expected_snumberSeconds of music time the generation is expected to take
progressnumber or null0 to 1 while running, when the model reports progress
song_idstring or nullThe song in the account's library once done
resultsarrayThe audio file once done; empty before
errorobject or nullcode and message of a generation that failed
created_at, started_at, finished_atstring or nullRFC 3339 times in UTC, with milliseconds

A result entry has url, content_type (audio/mpeg or audio/wav), duration_s, bytes and format. url is https://api.albastov.com/v1/music/generations/{id}/files/audio.mp3, or audio.wav for format wav, and needs a key with music:read. A generation runs in one queue with the songs made in the studio at https://music.albastov.com; a job of another kind made in the studio is no generation and answers 404.

Waiting

?wait=<s> on POST /v1/music/generations and GET /v1/music/generations/{id} holds the request until the generation settles. s is a number of seconds, 0 or more; a value over 85 counts as 85, and the hold ends 85 s after the request arrives at the latest. The answer is 200 once the generation is done, failed or cancelled, else 202 with the generation as it is. A song often takes longer than one hold: read the generation again with ?wait until state is done. An account holds 16 waiting requests at most; a further ?wait answers at once with 202 and the generation as it is.

Music time

Each account has a music time budget per UTC day and per UTC month, set by Albastar Corp. A generation reserves its expected_s when queued. A done generation counts the time it took, a failed generation counts nothing, and a generation cancelled while running counts the time it ran. A generation that would go over the day's or the month's remaining time fails with the one usage-limit error, 429 allowance_used with meter music.gpu_seconds, window day or month and previous_code over-daily or over-monthly; an account with no music time gets the same 429 with limit 0 and previous_code no-quota (The 429). The day resets at 00:00 UTC and the month on the 1st at 00:00 UTC. Songs made in the studio spend the same budget.

Create a generation

POST /v1/music/generations queues one song.

Retries

An Idempotency-Key header, or the idempotency_key field, makes a retry safe: 1 to 200 printable ASCII characters, such as a UUID. A second request of the same account with the same key and the same body answers the generation the first one queued, 202 or 200 as its state, and spends no more music time. The same key with another body fails with 409 idempotency-conflict. A key belongs to its account, without expiry; another account's key of the same value is unrelated.

Parameters

A JSON body. A field outside the list fails with 400 bad-param, param the field and message <field>: not a parameter.

NameTypeDescription
prompt requiredstringThe song's style: genre, mood, instruments, voice; 1 to 1,000 characters
lyricsstringThe words to sing, 6,000 characters at most; default ""
instrumentalbooleantrue makes a song without vocals and requires empty lyrics; default false
duration_snumberThe length in seconds, 10 to 300; on yue2, the longest the song may be. Without duration_s, the model picks the length
modelstringace (default) or yue2, from GET /v1/music/models
seedinteger1 to 2147483647; the same seed and parameters give a similar song. Without seed, a random seed, returned in params
formatstringThe file results lists: mp3 (default) or wav
titlestringThe song's title in the library, 80 characters at most; default ""
waitnumberQuery parameter: seconds to hold the request, 0 or more; 85 s at most from the request's arrival
idempotency_keystring1 to 200 printable ASCII characters; the same as the Idempotency-Key header when both are sent (Retries)

Request

curl

curl "https://api.albastov.com/v1/music/generations?wait=60" \
  -H "Authorization: Bearer $ALBASTAR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: folk-ballad-0001" \
  -d '{"prompt": "folk ballad, fingerpicked guitar, soft female voice",
       "lyrics": "[verse]\nThe river bends past the old mill",
       "duration_s": 60, "seed": 7}'

Python

import json
import os
import urllib.request

body = {
    "prompt": "folk ballad, fingerpicked guitar, soft female voice",
    "lyrics": "[verse]\nThe river bends past the old mill",
    "duration_s": 60,
    "seed": 7,
}
req = urllib.request.Request(
    "https://api.albastov.com/v1/music/generations?wait=60",
    data=json.dumps(body).encode(),
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
        "Content-Type": "application/json",
        "Idempotency-Key": "folk-ballad-0001",
    },
)
with urllib.request.urlopen(req, timeout=120) as res:
    gen = json.load(res)
print(gen["id"], gen["state"], gen["position"])

JavaScript

const res = await fetch(
  "https://api.albastov.com/v1/music/generations?wait=60",
  {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}`,
      "Content-Type": "application/json",
      "Idempotency-Key": "folk-ballad-0001",
    },
    body: JSON.stringify({
      prompt: "folk ballad, fingerpicked guitar, soft female voice",
      lyrics: "[verse]\nThe river bends past the old mill",
      duration_s: 60,
      seed: 7,
    }),
  },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const gen = await res.json();
console.log(gen.id, gen.state, gen.position);

Response

202 with the generation while it is queued or running; 200 once the generation settled within wait.

Response
{
  "id": "j-261104-r7w2bd",
  "object": "music.generation",
  "model": "ace",
  "state": "queued",
  "params": {
    "prompt": "folk ballad, fingerpicked guitar, soft female voice",
    "lyrics": "[verse]\nThe river bends past the old mill",
    "instrumental": false,
    "duration_s": 60,
    "seed": 7,
    "format": "mp3",
    "title": ""
  },
  "position": 2,
  "expected_s": 24,
  "progress": null,
  "song_id": null,
  "results": [],
  "error": null,
  "created_at": "2026-11-04T10:00:00.120Z",
  "started_at": null,
  "finished_at": null
}

The ids, positions and times differ on every generation.

Errors

StatusCodeMeaning
400bad-paramA field is unknown or out of range, lyrics is not empty with instrumental true, model is not in the model list, wait is not a number of seconds, or the idempotency key is empty, too long, not printable ASCII or differs between the header and the field; param names the field
400bad-jsonThe body is not a JSON object
403no-accessThe key's account has no music access
403storage-fullThe account's music library is full; deleting songs at https://music.albastov.com makes room
409idempotency-conflictThe account sent the same idempotency key earlier with another body
413too-largeThe body is over 1 MiB
429allowance_usedThe generation would go over the day's or the month's music time, or the account has none; previous_code is over-daily, over-monthly or no-quota
429too-many-jobsThe account has 50 generations and studio jobs queued or running

Get a generation

GET /v1/music/generations/{id} returns a generation of the key's account.

Parameters

NameTypeDescription
id requiredstringThe generation id j-…, in the path
waitnumberQuery parameter: seconds to hold the request until the generation settles, 0 or more; 85 s at most from the request's arrival

Request

curl

curl "https://api.albastov.com/v1/music/generations/$GEN?wait=60" \
  -H "Authorization: Bearer $ALBASTAR_API_KEY"

Python

import json
import os
import urllib.request

req = urllib.request.Request(
    f"https://api.albastov.com/v1/music/generations/{os.environ['GEN']}"
    "?wait=60",
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
    },
)
with urllib.request.urlopen(req, timeout=120) as res:
    gen = json.load(res)
print(gen["state"], [r["url"] for r in gen["results"]])

JavaScript

const res = await fetch(
  `https://api.albastov.com/v1/music/generations/${process.env.GEN}?wait=60`,
  { headers: { Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const gen = await res.json();
console.log(gen.state, gen.results.map((r) => r.url));

Response

200 with the generation object once settled, as in the Music overview; 202 when wait passes before the generation settles.

Errors

StatusCodeMeaning
400bad-paramwait is not a number, or is below 0
404not-foundNo generation with the id on the key's account

List generations

GET /v1/music/generations returns the account's generations, newest first.

Parameters

NameTypeDescription
limitintegerQuery parameter: generations per page, 1 to 100; default 20; a value over 100 counts as 100
cursorstringQuery parameter: the next_cursor of the previous page

Request

curl

curl "https://api.albastov.com/v1/music/generations?limit=10" \
  -H "Authorization: Bearer $ALBASTAR_API_KEY"

Python

import json
import os
import urllib.request

req = urllib.request.Request(
    "https://api.albastov.com/v1/music/generations?limit=10",
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
    },
)
with urllib.request.urlopen(req, timeout=30) as res:
    page = json.load(res)
for gen in page["generations"]:
    print(gen["id"], gen["state"], gen["params"]["prompt"])
print(page["next_cursor"])

JavaScript

const res = await fetch(
  "https://api.albastov.com/v1/music/generations?limit=10",
  { headers: { Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
const page = await res.json();
for (const gen of page.generations) {
  console.log(gen.id, gen.state, gen.params.prompt);
}
console.log(page.next_cursor);

Response

Response
{
  "generations": [
    {
      "id": "j-261104-r7w2bd",
      "object": "music.generation",
      "model": "ace",
      "state": "running"
    }
  ],
  "next_cursor": null
}

The response above shortens the generation to four fields. next_cursor is the id of the last generation on the page when another page follows, else null.

Errors

StatusCodeMeaning
400bad-paramlimit is not a whole number of 1 or more, or cursor is no generation of the account

Cancel a generation

DELETE /v1/music/generations/{id} cancels a queued or running generation. A generation cancelled while running counts the music time it ran.

Parameters

NameTypeDescription
id requiredstringThe generation id j-…, in the path

Request

curl

curl -X DELETE "https://api.albastov.com/v1/music/generations/$GEN" \
  -H "Authorization: Bearer $ALBASTAR_API_KEY"

Python

import json
import os
import urllib.request

req = urllib.request.Request(
    f"https://api.albastov.com/v1/music/generations/{os.environ['GEN']}",
    method="DELETE",
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
    },
)
with urllib.request.urlopen(req, timeout=30) as res:
    print(json.load(res)["state"])

JavaScript

const res = await fetch(
  `https://api.albastov.com/v1/music/generations/${process.env.GEN}`,
  {
    method: "DELETE",
    headers: { Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}` },
  },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log((await res.json()).state);

Response

200 with the generation object, state cancelled and finished_at set.

Errors

StatusCodeMeaning
404not-foundNo generation with the id on the key's account
409not-cancellableThe generation is already done, failed or cancelled

Download a file

GET /v1/music/generations/{id}/files/{name} returns the audio file of a done generation. The route answers Range requests with 206 and sends an ETag.

Parameters

NameTypeDescription
id requiredstringThe generation id j-…, in the path
name requiredstringaudio.mp3 or audio.wav, in the path; the url of a result names the file of the generation's format
downloadstringQuery parameter: 1 adds Content-Disposition: attachment with the song's title as the file name

Request

curl

curl -o song.mp3 \
  "https://api.albastov.com/v1/music/generations/$GEN/files/audio.mp3" \
  -H "Authorization: Bearer $ALBASTAR_API_KEY"

Python

import os
import urllib.request

req = urllib.request.Request(
    "https://api.albastov.com/v1/music/generations/"
    f"{os.environ['GEN']}/files/audio.mp3",
    headers={
        "User-Agent": "my-app/1.0",
        "Authorization": f"Bearer {os.environ['ALBASTAR_API_KEY']}",
    },
)
with urllib.request.urlopen(req, timeout=60) as res:
    open("song.mp3", "wb").write(res.read())

JavaScript

import { writeFile } from "node:fs/promises";

const res = await fetch(
  "https://api.albastov.com/v1/music/generations/" +
    `${process.env.GEN}/files/audio.mp3`,
  { headers: { Authorization: `Bearer ${process.env.ALBASTAR_API_KEY}` } },
);
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
await writeFile("song.mp3", Buffer.from(await res.arrayBuffer()));

Response

200 with the audio file, Content-Type audio/mpeg or audio/wav; 206 with the requested range.

Errors

StatusCodeMeaning
404not-foundNo generation with the id on the key's account, a generation not done, or a name other than audio.mp3 and audio.wav

List models

GET /v1/music/models returns the models a generation can run, without a key.

Request

curl

curl https://api.albastov.com/v1/music/models

Python

import json
import urllib.request

req = urllib.request.Request(
    "https://api.albastov.com/v1/music/models",
    headers={"User-Agent": "my-app/1.0"},
)
with urllib.request.urlopen(req, timeout=30) as res:
    for m in json.load(res)["models"]:
        print(m["id"], m["default"], m["available"], m["duration_s"])

JavaScript

const res = await fetch("https://api.albastov.com/v1/music/models");
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
for (const m of (await res.json()).models) {
  console.log(m.id, m.default, m.available, m.duration_s);
}

Response

Response
{
  "models": [
    {
      "id": "ace",
      "label": "ACE-Step",
      "default": true,
      "available": true,
      "duration_s": {"min": 10, "max": 300},
      "lyrics": true,
      "instrumental": true,
      "formats": ["mp3", "wav"],
      "typical_s": 45
    },
    {
      "id": "yue2",
      "label": "YuE2",
      "default": false,
      "available": true,
      "duration_s": {"min": 10, "max": 300},
      "lyrics": true,
      "instrumental": true,
      "formats": ["mp3", "wav"],
      "typical_s": 124
    }
  ]
}
FieldValue
id, labelThe model id model takes, and its display name
defaulttrue on the model a generation without model runs
availabletrue when the music service is online; a generation queued while available is false waits in the queue
duration_sThe range of duration_s, in seconds
lyrics, instrumentaltrue when the model sings lyrics and makes instrumentals
formatsThe values format takes
typical_sSeconds of music time a 120 s song takes on the model
Music generations · Albastar API docs