Music generation API
Send a description, get a song back as an MP3 and a WAV: instrumental, up to two minutes, royalty-free for commercial games and videos. The same songs as the generator: 10 a month free, 1,000 for $2 a month.
Quick start
- Get a key. Sign in on your Daydream dashboard, press Create, and copy the key (it's shown once).
- Make a song.
POST /v1/songswith a description. It comes back at once, in line. - Poll until it's done, every 2 seconds, then download
takes[0].mp3_url.
curl https://gen.daydream.live/v1/songs \
-H "Authorization: Bearer $DAYDREAM_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt": "a calm village theme with flute and harp", "loop": true}'
Base URL: https://gen.daydream.live/v1. Requests and responses are JSON. The OpenAPI spec describes every endpoint, for code generators and AI agents.
Keys
Send your Daydream API key on every request:
Authorization: Bearer <your key>
A key can do anything on its account, so keep it on your machine or your server: out of web pages, repositories and game builds you ship. Generate music in the editor, in a build step, or from your own backend, and ship the files.
Your songs
There are no credits. Each account has a number of songs a month, shared by the API and the generator page:
| Plan | Songs a month |
|---|---|
| Free | 10 |
| Songs, $2 a month | 1,000 |
| Studio Pro | 2,000 |
| Studio Max | 5,000 |
One POST /v1/songs is one song, with one or two variations. It counts when you create it; if it fails, it's given back. The count starts again on the first of each month (UTC). Pricing
Make a song
POST /v1/songs
| Field | Type | What it does |
|---|---|---|
prompt | string, required | What the song is, 1 to 400 characters: genre, mood, instruments, scene. Instrumental only: words asking for vocals or lyrics are dropped. Artist names are refused. |
duration | number | Seconds, 10 to 120. Default 120. Trimmed to whole bars with a short fade out (no fade on a loop). |
bpm | integer | 60 to 200. Default: a tempo the words name ("at 90 bpm"), else the genre's usual one, else 100. |
key | string | One of the 24 major and minor keys: "C major", "F# minor", "A minor"… Default: none. |
loop | boolean | A seamless loop: made to repeat, in whole bars, no fade. 8 bars unless duration says otherwise. Default false. |
seed | integer | 0 to 2147483647. Default random. Variation n uses seed + n − 1. |
variations | 1 or 2 | How many versions. Default 1. Either way it's one song. |
It answers 202 with the song, queued:
{
"id": "sng_4hT2kq9XbV1nLm0pQe3rZa",
"status": "queued",
"position": 2,
"prompt": "a calm village theme with flute and harp",
"duration": 120,
"bpm": 70,
"key": null,
"loop": true,
"seed": 482113,
"variations": 1,
"created_at": "2026-10-15T09:12:03Z",
"finished_at": null,
"error": null,
"takes": []
}
Get a song
GET /v1/songs/{id}
Poll it every 2 seconds until status is done or failed:
| status | Means |
|---|---|
queued | In line. position is its place (1 is next). |
rendering | Being made. |
done | Ready: takes has one entry per variation. |
failed | It didn't finish. error says why; the song is given back. |
{
"id": "sng_4hT2kq9XbV1nLm0pQe3rZa",
"status": "done",
"position": null,
…
"finished_at": "2026-10-15T09:12:15Z",
"takes": [
{
"n": 1,
"seed": 482113,
"duration": 109.7,
"waveform": [0.21, 0.34, 0.52, …],
"mp3_url": "https://gen.daydream.live/v1/songs/sng_4hT2kq9XbV1nLm0pQe3rZa/1.mp3",
"wav_url": "https://gen.daydream.live/v1/songs/sng_4hT2kq9XbV1nLm0pQe3rZa/1.wav"
}
]
}
Another account's song answers 404.
List your songs
GET /v1/songs?limit=20&cursor=…
Newest first. limit is 1 to 100 (default 20). Pass the next_cursor you got back for the next page; it's null on the last one.
{ "songs": [ … ], "next_cursor": "eyJ0IjoxNzYw…" }
Usage
GET /v1/usage
{ "plan": "songs", "limit": 1000, "used": 212, "resets_at": "2026-11-01T00:00:00Z" }
plan is one of free, songs, pro, max, internal. limit is null when it's unlimited.
Audio links
/v1/songs/{id}/{n}.mp3 and /v1/songs/{id}/{n}.wav redirect to the file. They need no key and they don't expire, so you can store them and load them straight from a game engine or an <audio> tag. Anyone with a link can download that file. Follow redirects (curl -L).
MP3 is 192 kbps, WAV is 16-bit; both are stereo at 48 kHz.
Errors
Every error is JSON with a code to branch on and a sentence to show:
{
"error": "limit",
"message": "You've made all 10 of this month's songs.",
"limit": 10,
"used": 10,
"plan": "free",
"upgrade_url": "https://gen.daydream.live/pricing"
}
| Status | error | When |
|---|---|---|
| 400 | invalid | A field is wrong; the message names it. |
| 401 | unauthorized | No key, or a key that doesn't work. |
| 402 | limit | This month's songs are used up. Comes with limit, used, plan and upgrade_url. |
| 404 | not_found | No such song on this account. |
| 422 | prompt_rejected | The prompt names an artist. Describe the sound instead. |
| 429 | too_many | Three of your songs are already in line or being made. Wait for one to finish. |
| 503 | unavailable | The generator is down for a moment. Try again shortly. |
How fast
Songs are made one after another on a dedicated GPU: a song takes a few seconds once it's next, and a busy moment adds a short wait in line (position tells you where you are). Poll every 2 seconds; there's no need to poll faster. You can have up to three songs in line or being made at once.
Examples
curl
API=https://gen.daydream.live/v1
AUTH="Authorization: Bearer $DAYDREAM_API_KEY"
ID=$(curl -s $API/songs -H "$AUTH" -H "Content-Type: application/json" \
-d '{"prompt": "a tense boss fight with taiko drums and brass", "variations": 2}' | jq -r .id)
while :; do
STATUS=$(curl -s $API/songs/$ID -H "$AUTH" | jq -r .status)
[ "$STATUS" = done ] || [ "$STATUS" = failed ] && break
sleep 2
done
curl -sL $API/songs/$ID/1.mp3 -o boss-1.mp3
curl -sL $API/songs/$ID/2.mp3 -o boss-2.mp3
JavaScript
Node 18 or later, or any runtime with fetch. Keep the key on the server.
const API = "https://gen.daydream.live/v1";
const KEY = process.env.DAYDREAM_API_KEY;
async function api(path, init = {}) {
const r = await fetch(API + path, {
...init,
headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
});
const body = await r.json();
if (!r.ok) throw new Error(`${body.error}: ${body.message}`);
return body;
}
let song = await api("/songs", {
method: "POST",
body: JSON.stringify({ prompt: "an open-world exploration theme with acoustic guitar", loop: true }),
});
while (song.status === "queued" || song.status === "rendering") {
await new Promise((r) => setTimeout(r, 2000));
song = await api(`/songs/${song.id}`);
}
if (song.status === "failed") throw new Error(song.error.message);
console.log(song.takes[0].mp3_url);
Python
import os, time, requests
API = "https://gen.daydream.live/v1"
HEADERS = {"Authorization": f"Bearer {os.environ['DAYDREAM_API_KEY']}"}
r = requests.post(f"{API}/songs", headers=HEADERS, json={"prompt": "an upbeat 8-bit chiptune level theme", "loop": True})
r.raise_for_status()
song = r.json()
while song["status"] in ("queued", "rendering"):
time.sleep(2)
r = requests.get(f"{API}/songs/{song['id']}", headers=HEADERS)
r.raise_for_status()
song = r.json()
if song["status"] == "failed":
raise RuntimeError(song["error"]["message"])
with open("level.mp3", "wb") as f:
f.write(requests.get(song["takes"][0]["mp3_url"]).content)
Unity (C#)
A component that makes a song and plays it on loop. Use it in the editor or a tool; don't ship your key in a build.
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
public class GenerateMusic : MonoBehaviour
{
const string Api = "https://gen.daydream.live/v1";
[SerializeField] string apiKey;
[SerializeField] string prompt = "a calm village theme with flute and harp";
[SerializeField] AudioSource source;
[System.Serializable] class Create { public string prompt; public bool loop; }
[System.Serializable] class Take { public int n; public float duration; public string mp3_url; public string wav_url; }
[System.Serializable] class Song { public string id; public string status; public Take[] takes; }
IEnumerator Start()
{
Song song = null;
var body = Encoding.UTF8.GetBytes(JsonUtility.ToJson(new Create { prompt = prompt, loop = true }));
using (var req = new UnityWebRequest(Api + "/songs", "POST"))
{
req.uploadHandler = new UploadHandlerRaw(body);
req.downloadHandler = new DownloadHandlerBuffer();
req.SetRequestHeader("Content-Type", "application/json");
req.SetRequestHeader("Authorization", "Bearer " + apiKey);
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success) { Debug.LogError(req.downloadHandler.text); yield break; }
song = JsonUtility.FromJson<Song>(req.downloadHandler.text);
}
while (song.status == "queued" || song.status == "rendering")
{
yield return new WaitForSeconds(2);
using (var req = UnityWebRequest.Get(Api + "/songs/" + song.id))
{
req.SetRequestHeader("Authorization", "Bearer " + apiKey);
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success) { Debug.LogError(req.downloadHandler.text); yield break; }
song = JsonUtility.FromJson<Song>(req.downloadHandler.text);
}
}
if (song.status != "done") { Debug.LogError("The song didn't finish."); yield break; }
using (var req = UnityWebRequestMultimedia.GetAudioClip(song.takes[0].mp3_url, AudioType.MPEG))
{
yield return req.SendWebRequest();
if (req.result != UnityWebRequest.Result.Success) { Debug.LogError(req.error); yield break; }
source.clip = DownloadHandlerAudioClip.GetContent(req);
source.loop = true;
source.Play();
}
}
}
Godot 4 (GDScript)
Attach to a node with an AudioStreamPlayer child. As with Unity, keep the key out of exported builds.
extends Node
const API := "https://gen.daydream.live/v1"
@export var api_key := ""
@export var prompt := "a calm village theme with flute and harp"
@onready var player: AudioStreamPlayer = $AudioStreamPlayer
func _ready() -> void:
var song: Dictionary = await _call("/songs", HTTPClient.METHOD_POST, {"prompt": prompt, "loop": true})
while song.get("status") in ["queued", "rendering"]:
await get_tree().create_timer(2.0).timeout
song = await _call("/songs/%s" % song["id"])
if song.get("status") != "done":
push_error("The song didn't finish: %s" % song)
return
var stream := AudioStreamMP3.new()
stream.data = await _download(song["takes"][0]["mp3_url"])
stream.loop = true
player.stream = stream
player.play()
func _call(path: String, method := HTTPClient.METHOD_GET, body = null) -> Dictionary:
var http := HTTPRequest.new()
add_child(http)
var headers := PackedStringArray(["Authorization: Bearer %s" % api_key, "Content-Type: application/json"])
http.request(API + path, headers, method, JSON.stringify(body) if body != null else "")
var res: Array = await http.request_completed
http.queue_free()
return JSON.parse_string(res[3].get_string_from_utf8())
func _download(url: String) -> PackedByteArray:
var http := HTTPRequest.new()
add_child(http)
http.request(url)
var res: Array = await http.request_completed
http.queue_free()
return res[3]
More
- OpenAPI spec (3.1) and llms.txt, for code generators and AI agents.
- Questions and bugs: Discord, or hello@daydream.live.
- Need the live engine (sessions you play into, session players, Styles)? That's the Studio API.