AI music API in TypeScript: generate, wait and save an MP3
Call Sume's Music Router from TypeScript: generateMusicRouter, waitForJob, then download the audio artifact. $0.125 per track, Google lists Lyria 3.5 at $0.08.

To generate AI music from TypeScript, install @sume-com/sdk, call generateMusicRouter with a prompt, wait with waitForJob, and download the audio artifact from the finished job. Sume charges a fixed $0.125 per accepted Music generation, whatever the prompt length. Google's Gemini API pricing page, read on 2026-10-05, lists Lyria 3.5 at $0.08 per song with no free tier.
The SDK side comes from the TypeScript SDK page and Waiting for runs and jobs; the request fields come from the Music Router docs. The SDK is @sume-com/sdk@0.2.0, has no runtime dependencies, and sends x-api-key only, so do not add an Authorization header yourself.
The whole script
This runs on Node 18+ as an ES module. It submits with mode: "async", which the SDK docs recommend for waitForJob, then reads the audio artifact and writes it to disk. model is optional: omit it or send sume/music-auto and Sume picks the engine (Lyria 3.5 today); send lyria-3.5 or lyria-3-pro to pin one.
import { writeFile } from "node:fs/promises";
import { createSumeClient, generateMusicRouter, waitForJob } from "@sume-com/sdk";
async function main() {
const apiKey = process.env.SUME_API_KEY;
if (!apiKey) throw new Error("Set SUME_API_KEY first");
const client = createSumeClient({ apiKey });
const { data, error } = await generateMusicRouter({
client,
headers: { "Idempotency-Key": "launch-bed-001" },
body: {
model: "sume/music-auto",
prompt:
"Bright indie-pop bed, 112 BPM, G major. Muted guitar, claps, " +
"warm bass. Builds at 0:15. A 30-second track. Instrumental, no vocals.",
mode: "async",
},
});
if (error || !data) throw new Error(JSON.stringify(error));
const job = await waitForJob(data.data.request_id, { client });
if (job.status !== "completed") {
throw new Error(`music job ${job.id} ended as ${job.status}`);
}
const audio = job.result?.artifacts?.find((a) => a.type === "audio");
if (!audio?.url) throw new Error("no audio artifact on the job");
const file = await fetch(audio.url);
await writeFile("bed.mp3", Buffer.from(await file.arrayBuffer()));
console.log("saved bed.mp3 from job", job.id);
}
main().catch((err) => {
console.error(err);
process.exit(1);
});What each step does
Idempotency-Keymakes a retry of the same submit return the original job instead of a second charge. Use one key per track you intend to make, not one per attempt.waitForJobpolls/v1/jobs/:id/statusevery 2 seconds at minimum, or longer when the server'snext_poll_after_secondsasks for it. Its default deadline is 20 minutes.- It resolves for failed and canceled jobs too, so the script checks
job.statusrather than relying on a catch block. - The audio is in
result.artifacts[]wheretypeisaudio, usuallyaudio/mpegonmedia.sume.com. Raw provider URLs are not public outputs. - The raw job JSON carries
request.routed_model, which names the engine that ran when you submittedsume/music-auto.
Limits to design around
There is no duration field and no negative_prompt: Sume rejects duration and duration_seconds, and a non-empty negative_prompt returns a 400. Put the length in the prompt ("a 30-second track") and the exclusions in the positive text ("Instrumental, no vocals"). Prompts run 1 to 5,000 characters.
A waitForJob timeout does not cancel the job. It keeps running and still bills, so store the job id and read it again with getApiJob later instead of resubmitting under a new key.
| Field | Value in the script | Rule |
|---|---|---|
| model | sume/music-auto | Omit it for the same default; lyria-3.5 and lyria-3-pro pin an engine |
| prompt | 30-second indie-pop brief | 1 to 5,000 characters |
| mode | async | Pair with waitForJob; sync and subscribe cap at 30 seconds |
| Idempotency-Key | launch-bed-001 | Same key and payload returns the original job |
Next
Once the file is saved, put it under narration with a Timeline soundtrack block, which takes gain_db, loop, fade_out_seconds and duck_db. See Add background music to a video with an API.
Sources
Related posts
More in Developers
- TypeScript exhaustive switch over a Sume run's terminal status
A run ends as completed, failed, canceled or skipped, and the last two send no webhook. Use a never check so a new status fails the build.
- TypeScript webhook verifier for Wan 3.0 clips: refuse an empty secret
A TypeScript verifier for Sume webhooks: HMAC SHA 256 over timestamp.body, rotation entries, a 5 minute replay window and a hard refusal of an empty secret.
- Ukrainian text to speech API: set language uk and price a script
Cartesia Sonic 3.6 lists Ukrainian. Send language uk to Sume TTS 1.0, handle the 409 voice mismatch, and price a 5,000-character script at $0.24.
- usage_reservation_unavailable: a Sume job failed before it started
The usage reservation could not be placed, so Sume failed the queued job and gave back any hold. Nothing ran. Resubmit with the same Idempotency-Key.
Written by Sume