AI music generator API in Python: prompt to MP3 file

A short Python script that sends a prompt to Sume's Music Router, polls the job and saves the audio. Runs with httpx and asyncio.

4 min readSume
All posts

To generate music from Python, POST a prompt to https://api.sume.com/v1/music-router/generate, poll the job's status_url until it is terminal, then read the audio artifact from the result and download it. The script below does that with httpx and asyncio and saves an MP3.

Request and job shapes are from the Music Router and Jobs and results docs, read 2026-09-29.

What is the script?

Set SUME_API_KEY first. The Idempotency-Key makes a retry of the same submit return the original job instead of billing a second one.

import asyncio
import os
import httpx

HEADERS = {"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"}
BODY = {
    "prompt": "Warm lo-fi hip hop, 84 BPM, C minor. Dusty Rhodes, brushed drums. "
    "A 30-second track. Instrumental, no vocals."
}


async def main() -> None:
    async with httpx.AsyncClient(headers=HEADERS, timeout=60) as client:
        r = await client.post(
            "https://api.sume.com/v1/music-router/generate",
            json=BODY,
            headers={"Idempotency-Key": "music-py-001"},
        )
        r.raise_for_status()
        job = r.json()["data"]
        while not (s := (await client.get(job["status_url"])).json()["data"])["terminal"]:
            await asyncio.sleep(s.get("next_poll_after_seconds") or 3)
        if s["sume_status"] != "completed":
            raise SystemExit(f"job ended as {s['sume_status']}")
        res = (await client.get(job["result_url"])).json()["data"]["result"]
        audio = next(a for a in res["artifacts"] if a["type"] == "audio")
        open("track.mp3", "wb").write((await client.get(audio["url"])).content)


asyncio.run(main())

What does each step read?

From the Sume Music Router and Jobs docs, read 2026-09-29.
StepDetail
SubmitPOST /v1/music-router/generate; default mode is async
Pollstatus_url until terminal; honor next_poll_after_seconds
Resultresult_url once result_ready; audio is in result.artifacts[], type audio
FileTypically audio/mpeg on media.sume.com

What can go wrong?

  • A non-empty negative_prompt or any duration field returns HTTP 400; leave both out.
  • Do not resubmit because your process timed out; poll the same job. Each new generation is billed.
  • For waits you do not want to hold, use mode: "webhook" with a public HTTPS webhook_url and keep polling as a backup.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume