Tunova

Suno API in Python & Node.js

The Suno API flow is the same in any language: submit a generation (you get a job_id back immediately), then poll until it’s done — or pass a callback_url and take a webhook. Generation takes 1–3 minutes, so it’s always async. The fastest path is the official SDK, which does submit → poll in one call; if you’d rather add no dependency, the complete raw-HTTP functions are right below.

The fast path: the official SDK

The Tunova SDK (MIT, tiny) turns the whole submit → poll flow into one generate() call and ships a built-in HMAC webhook verifier.pip install tunova or npm i tunova:

from tunova import Tunova # pip install tunova t = Tunova("sk_live_…") # .generate() submits AND polls for you — one call from prompt to audio URL job = t.generate("calm rainy-night lofi", model="v5.5") print(job["clips"][0]["audio_url"] if job["status"] == "complete" else job["error"]) # a failed render auto-refunds. Prefer async? t.submit(..., callback_url="https://you/hook")
import { Tunova } from "tunova"; // npm i tunova const t = new Tunova(process.env.TUNOVA_API_KEY!); // .generate() submits AND polls for you const job = await t.generate("calm rainy-night lofi", { model: "v5.5" }); console.log(job.status === "complete" ? job.clips[0]?.audio_url : job.error);

Raw HTTP — no dependencies

Same flow, hand-rolled with a timeout and proper error handling — Python’s requests and Node 18+’s built-in fetch, nothing else.

Python

import os, time, requests # pip install requests API = "https://api.tunova.ai" H = {"X-API-Key": os.environ["TUNOVA_API_KEY"]} def generate(prompt, model="v5.5", timeout=300): r = requests.post(f"{API}/api/generate", headers=H, json={"prompt": prompt, "model": model}) r.raise_for_status() job_id = r.json()["job_id"] # 202 + job_id deadline = time.time() + timeout while time.time() < deadline: job = requests.get(f"{API}/api/jobs/{job_id}", headers=H).json() if job["status"] == "complete": return job["clips"][0]["audio_url"] if job["status"] == "failed": raise RuntimeError(job["error"]) # tokens auto-refunded time.sleep(3) raise TimeoutError(job_id) print(generate("calm rainy-night lofi"))

Node.js / TypeScript

const API = "https://api.tunova.ai"; const H = { "X-API-Key": process.env.TUNOVA_API_KEY, "Content-Type": "application/json" }; async function generate(prompt, model = "v5.5", timeoutMs = 300_000) { const res = await fetch(`${API}/api/generate`, { method: "POST", headers: H, body: JSON.stringify({ prompt, model }), }); if (!res.ok) throw new Error(`${res.status} ${await res.text()}`); const { job_id } = await res.json(); // 202 + job_id const deadline = Date.now() + timeoutMs; while (Date.now() < deadline) { const job = await (await fetch(`${API}/api/jobs/${job_id}`, { headers: H })).json(); if (job.status === "complete") return job.clips[0].audio_url; if (job.status === "failed") throw new Error(job.error); // tokens auto-refunded await new Promise((r) => setTimeout(r, 3000)); } throw new Error(`timeout ${job_id}`); } console.log(await generate("calm rainy-night lofi"));

Notes

  • Model: pass model = v5.5. Omit it to use the account default.
  • Custom / lyrics mode: POST to /api/custom_generate with prompt= your lyrics, plus optional tags (style) and title.
  • Billed only on success: a failed job auto-refunds the tokens — your except/catch branch is for logging, not for chasing a refund.
  • Idempotency: send an Idempotency-Key header and a retried submit returns the same job instead of charging twice.

Get a key (50 free tokens, no card) and the full reference on the quickstart, or browse the interactive API docs.

Tunova is an independent service, not affiliated with or endorsed by Suno.