Tunova Suno API quickstart
A finished song in three lines. Generation is async — submit returns a job_id, then you poll it or get an HMAC-signed webhook. You’re billed only when a song actually delivers; a failed render refunds itself.
Need a key? Sign up free — 50 tokens (≈ 5 songs), no card.
# The official SDK submits AND polls for you — prompt → audio URL in one call.
# Python · pip install tunova
from tunova import Tunova
t = Tunova("sk_live_…")
job = t.generate("calm rainy-night lofi", model="v5.5")
print(job["clips"][0]["audio_url"] if job["status"] == "complete" else job["error"])
# Node / TS · npm i tunova
import { Tunova } from "tunova";
const t = new Tunova(process.env.TUNOVA_API_KEY!);
const job = await t.generate("calm rainy-night lofi", { model: "v5.5" });
console.log(job.status === "complete" ? job.clips[0]?.audio_url : job.error);
# A failed render auto-refunds — you never pay for it. Built-in HMAC webhook verifier included;
# prefer async delivery? use t.submit(..., callback_url="https://you/hook") instead of polling.
# 1 — submit: returns 202 + a job_id (costs 10 tokens, settled on success)
curl https://api.tunova.ai/api/generate \
-H "X-API-Key: $TUNOVA_API_KEY" \
-H "Content-Type: application/json" \
-d '{"prompt":"calm rainy-night lofi","model":"v5.5"}'
# → {"job_id":"8f3a…","status":"queued","status_url":"/api/jobs/8f3a…"}
# 2 — poll until terminal (or pass "callback_url" for an HMAC-signed webhook instead)
curl https://api.tunova.ai/api/jobs/8f3a… -H "X-API-Key: $TUNOVA_API_KEY"
# → {"status":"complete","clips":[{"audio_url":"https://…mp3","duration":187.4}]}
# a failed render auto-refunds the 10 tokens — you never pay for it.
import os, time, requests # pip install requests
API = "https://api.tunova.ai"
H = {"X-API-Key": os.environ["TUNOVA_API_KEY"]}
# submit → 202 + job_id (10 tokens, settled on success)
job = requests.post(f"{API}/api/generate", headers=H,
json={"prompt": "calm rainy-night lofi", "model": "v5.5"}).json()
# poll until terminal (or pass "callback_url" for an HMAC-signed webhook)
while job["status"] not in ("complete", "failed"):
time.sleep(3)
job = requests.get(f"{API}/api/jobs/{job['job_id']}", headers=H).json()
print(job["clips"][0]["audio_url"] if job["status"] == "complete" else job["error"])
// Node 18+ — global fetch, zero dependencies
const API = "https://api.tunova.ai";
const H = { "X-API-Key": process.env.TUNOVA_API_KEY, "Content-Type": "application/json" };
// submit → 202 + job_id
let job = await (await fetch(`${API}/api/generate`, {
method: "POST", headers: H,
body: JSON.stringify({ prompt: "calm rainy-night lofi", model: "v5.5" }),
})).json();
// poll until terminal (or pass "callback_url" for an HMAC-signed webhook)
while (!["complete", "failed"].includes(job.status)) {
await new Promise((r) => setTimeout(r, 3000));
job = await (await fetch(`${API}/api/jobs/${job.job_id}`, { headers: H })).json();
}
console.log(job.status === "complete" ? job.clips[0].audio_url : job.error);
# Give any MCP client (Claude, Cursor, …) the ability to generate music.
# Same API key, same billed-on-success rule.
claude mcp add --transport http tunova https://api.tunova.ai/mcp \
--header "X-API-Key: sk_live_…"
# …or paste this into your MCP client config (mcp.json / settings):
{
"mcpServers": {
"tunova": {
"url": "https://api.tunova.ai/mcp",
"headers": { "X-API-Key": "sk_live_…" }
}
}
}
# Then just ask: "generate a calm lofi track"
# tools: generate_song · wait_for_song · check_song
Reference
- That’s the whole API — submit, then poll or take a webhook. Full interactive reference: api.tunova.ai/docs · machine spec /openapi.json.
- Prefer a tidy wrapper? Zero-dependency Python & Node SDKs (+ MCP manifest): github.com/erliona/tunova-sdk.
- Model: v5.5 (per-request model param). Custom/lyrics mode: POST /api/custom_generate with prompt = your lyrics.
- Webhooks are HMAC-signed (X-Webhook-Signature: sha256=… over <timestamp>.<body>) so you can verify every callback is really from us.