Generate music
Music generation produces royalty-free background tracks from a description of the mood, such as “upbeat corporate”, “calm ambient” or “energetic hip-hop”.
The flow
POST /v1/ai/generate-music ← describe the style and duration
GET /v1/jobs/{id} ← poll until completed
The script
Set the length with duration_seconds, not with words in the prompt. It takes one of 5, 10, 15, 20 or 30 and defaults to 15.
The polling helpers used below
const API_KEY = "YOUR_API_KEY";
const API = "https://api.rendley.com/v1";
const headers = {
"Authorization": `Bearer ${API_KEY}`,
"Content-Type": "application/json",
};
// Both endpoints share these three terminal statuses. They differ only in
// their in-progress names: queued/processing vs pending/running.
const TERMINAL = ["completed", "failed", "canceled"];
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
// Generation and export jobs. The finished job carries output.url.
async function waitForJob(jobId, interval = 5000) {
let job = null;
while (job === null || !TERMINAL.includes(job.status)) {
await sleep(interval);
const response = await fetch(`${API}/jobs/${jobId}`, { headers });
if (!response.ok) {
throw new Error("Job lookup failed: " + response.status);
}
const body = await response.json();
job = body.data;
console.log("Job status:", job.status);
}
if (job.status !== "completed") {
throw new Error("Job " + job.status + ": " + (job.error || ""));
}
return job;
}
// Agent jobs. The finished job carries project_id, thread_id and
// last_message, but no output: export the project to get a file.
// onPause runs when an interactive run stops to ask something.
async function waitForAgentJob(jobId, interval = 5000, onPause) {
let job = null;
while (job === null || !TERMINAL.includes(job.status)) {
// This endpoint long-polls. Sleep anyway, so a fast response
// cannot turn this into a tight request loop.
await sleep(interval);
const response = await fetch(`${API}/agent/jobs/${jobId}`, { headers });
if (!response.ok) {
throw new Error("Job lookup failed: " + response.status);
}
const body = await response.json();
job = body.data;
console.log("Edit status:", job.status);
// Only interactive runs reach this; unattended runs never pause.
if (job.status === "waiting_input" && onPause) {
await onPause(job);
}
}
if (job.status !== "completed") {
throw new Error("The edit did not finish: " + (job.error || job.reason));
}
return job;
}import time
import requests
API_KEY = "YOUR_API_KEY"
API = "https://api.rendley.com/v1"
HEADERS = {"Authorization": f"Bearer {API_KEY}"}
# Both endpoints share these three terminal statuses. They differ only in
# their in-progress names: queued/processing vs pending/running.
TERMINAL = {"completed", "failed", "canceled"}
def wait_for_job(job_id, interval=5):
"""Generation and export jobs. The finished job carries output.url."""
job = None
while job is None or job["status"] not in TERMINAL:
time.sleep(interval)
response = requests.get(f"{API}/jobs/{job_id}", headers=HEADERS)
response.raise_for_status()
job = response.json()["data"]
print("Job status:", job["status"])
if job["status"] != "completed":
raise RuntimeError(f"Job {job['status']}: {job.get('error', '')}")
return job
def wait_for_agent_job(job_id, interval=5, on_pause=None):
"""Agent jobs. The finished job carries project_id, thread_id and
last_message, but no output: export the project to get a file.
on_pause runs when an interactive run stops to ask something.
"""
job = None
while job is None or job["status"] not in TERMINAL:
# This endpoint long-polls. Sleep anyway, so a fast response
# cannot turn this into a tight request loop.
time.sleep(interval)
response = requests.get(f"{API}/agent/jobs/{job_id}", headers=HEADERS)
response.raise_for_status()
job = response.json()["data"]
print("Edit status:", job["status"])
# Only interactive runs reach this; unattended runs never pause.
if job["status"] == "waiting_input" and on_pause:
on_pause(job)
if job["status"] != "completed":
raise RuntimeError("The edit did not finish: " + (job.get("error") or job.get("reason", "")))
return jobconst PROMPT = [
"Upbeat, inspiring corporate background music.",
"Light piano and soft drums, building energy.",
].join(" ");
// Queue the track. This returns before any audio is rendered.
async function startMusicJob() {
const response = await fetch(`${API}/ai/generate-music`, {
method: "POST",
headers,
body: JSON.stringify({
params: { prompt: PROMPT, duration_seconds: 30 },
}),
});
if (!response.ok) {
throw new Error("Could not start the job: " + response.status);
}
const payload = await response.json();
return payload.data.job_id;
}
const jobId = await startMusicJob();
const job = await waitForJob(jobId);
// The signed URL is the result. Fetch it, pipe it to your storage,
// or hand it to the browser.
console.log(job.output.url);import requests
PROMPT = (
"Upbeat, inspiring corporate background music. "
"Light piano and soft drums, building energy."
)
def start_music_job():
"""Queue the track. Returns before any audio is rendered."""
response = requests.post(
f"{API}/ai/generate-music",
headers=HEADERS,
json={"params": {"prompt": PROMPT, "duration_seconds": 30}},
)
response.raise_for_status()
return response.json()["data"]["job_id"]
job_id = start_music_job()
job = wait_for_job(job_id)
# The finished job points at a signed URL. Download it before it expires.
audio = requests.get(job["output"]["url"])
audio.raise_for_status()
with open("background-music.mp3", "wb") as f:
f.write(audio.content)
print("Saved background-music.mp3")Also available
POST /v1/ai/generate-sound-effectgenerates short sound effects (“whoosh”, “notification chime”, “door slam”).