Rendley docs

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 job
const 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-effect generates short sound effects (“whoosh”, “notification chime”, “door slam”).