Documentazione
Generazione musicale
Da testo a musica con Suno V5 / V5.5 — voce, testi e controllo dello stile. Due formati API: un endpoint sincrono singolo che attende la disponibilità dei brani e una coppia di API asincrone con invio e polling (con webhook firmati) per l’uso in produzione.
Endpoint: POST /v1/audio/music (sincrono, descritto per primo) e POST /v1/audio/music/jobs + GET /v1/audio/music/jobs/{id} (asincroni, consigliati per la produzione). Ogni richiesta genera due brani; la generazione richiede circa 1–3 minuti. Prezzo: $0.09 per richiesta di generazione su suno-v5, $0.09 su suno-v5-5 — entrambi i brani vengono fatturati una sola volta, usando il saldo prepagato; una generazione non riuscita non viene fatturata.
Avvio rapido (sincrono)
Invia un prompt e un modello Suno. La richiesta resta in attesa finché entrambi i brani non sono pronti, poi restituisce i relativi URL permanenti.
import requests
# Synchronous: one request blocks until the tracks are ready (1-3 min).
resp = requests.post(
"https://api.kunavo.com/v1/audio/music",
headers={"Authorization": f"Bearer {API_KEY}"},
json={
"model": "suno-v5",
"prompt": "upbeat lofi hip hop for late-night coding, mellow piano",
"instrumental": True,
},
timeout=600, # generation can take a few minutes
)
# Suno returns ~2 tracks per request.
for track in resp.json()["data"]:
print(track["url"], track.get("image_url"))Parametri
| Parametro | Tipo | Note |
|---|---|---|
model | stringa (obbligatorio) | Uno slug di modello musicale: suno-v5 o suno-v5-5. |
prompt | stringa (obbligatorio) | Descrizione della musica (o testo della canzone, in modalità personalizzata). |
instrumental | bool | true = senza voce. Predefinito false. |
customMode | bool | Modalità Suno avanzata: controlla esplicitamente struttura, testo e stile. |
style | string | Indicazione di stile o genere, ad es. «lofi, jazz, chill». |
title | string | Titolo del brano. |
webhook_url | string (https) | Solo asincrono — invia qui un evento terminale firmato. Vedi Webhooks. |
Modelli musicali
| Slug | Provider | Note |
|---|---|---|
suno-v5 | Suno | Suno V5 — voce, testi e fusione di stili. |
suno-v5-5 | Suno | Suno V5.5 — la versione più recente, con maggiore fedeltà e generazioni più lunghe. |
Consulta /models per l’elenco aggiornato e i prezzi per chiamata.
API asincrona (invio + polling)
Endpoint: POST /v1/audio/music/jobs + GET /v1/audio/music/jobs/{id}. Ricalca l’API asincrona per video, quindi lo stesso codice di polling e webhook funziona per entrambe le modalità.
L’endpoint sincrono mantiene aperta una connessione HTTP per minuti: è comodo per gli script, ma inaffidabile su dispositivi mobili. La coppia asincrona restituisce subito un ID msc_*; esegui il polling di GET /v1/audio/music/jobs/{id} finché status non è completed o failed. Stessi modelli, stessi prezzi, stessi URL CDN permanenti.
import requests, time
# 1. Submit — returns immediately with a msc_ task id. No long-lived connection.
submit = requests.post(
"https://api.kunavo.com/v1/audio/music/jobs",
headers={
"Authorization": f"Bearer {API_KEY}",
# Optional: retrying with the same key inside ~24h returns the
# original task rather than submitting again.
"Idempotency-Key": "my-song-uuid",
},
json={
"model": "suno-v5",
"prompt": "dreamy synthwave with a driving bassline",
"title": "Midnight Drive",
},
timeout=60,
).json()
task_id = submit["id"] # "msc_abc..."
status = submit["status"] # "queued"
# 2. Poll until terminal. Recommended cadence: 5s, backing off to 30s.
while status not in ("completed", "failed"):
time.sleep(5)
r = requests.get(
f"https://api.kunavo.com/v1/audio/music/jobs/{task_id}",
headers={"Authorization": f"Bearer {API_KEY}"},
timeout=30,
).json()
status = r["status"]
if status == "failed":
raise RuntimeError(r["error"]["message"])
for track in r["output"]["tracks"]:
print(track["url"])Struttura della risposta
| Campo | Tipo | Note |
|---|---|---|
id | string | ID attività con prefisso msc_. |
object | string | "music". |
status | string | queued | in_progress | completed | failed. |
model | string | Lo slug del modello inviato. |
created_at | int | Secondi Unix in cui abbiamo accettato l’invio. |
completed_at | int | null | Secondi Unix del termine dell’attività; null mentre è in corso. |
expires_at | int | Secondi Unix, 30 giorni dopo l’invio: l’attività può essere interrogata almeno fino ad allora. Gli URL dei suoi risultati non scadono. |
progress | int | 0 finché è in corso, poi 100. |
output | object | null | {tracks[], archived} al completamento. |
output. | array | Ogni elemento: {url, stream_url, image_url}. url / image_url sono link CDN permanenti. |
error | object | null | {code, message} in caso di errore. |
Intestazioni e convenzioni
Idempotency-Key(facoltativo, ≤128 caratteri) — inviare due volte la stessa chiave per lo stesso account restituisce l’attività originale invece di crearne una duplicata.POST /v1/audio/music/jobsrestituisce 202 Accepted per un nuovo invio e 200 OK quando una chiave di idempotenza ripropone un risultato.GETè associato al proprietario: una richiesta per un’attività appartenente a un altro account restituisce 404.- Frequenza di polling consigliata: 5 s, aumentando gradualmente fino a 30 s.
Webhook
Al momento dell’invio, passa webhook_url (https); Kunavo invia una richiesta POST firmata all’evento music.completed / music.failed appena l’attività termina, con consegna e nuovi tentativi a intervalli crescenti. Il campo data dell’evento corrisponde esattamente al payload GET riportato sopra.
# Pass webhook_url on submit to be pushed a signed event on terminal —
# instead of (or alongside) polling:
# {"model": "suno-v5", "prompt": "...", "webhook_url": "https://you.com/hook"}
#
# Verify the HMAC signature on your receiver (Flask shown):
import hmac, hashlib
from flask import request, abort
def verify(secret: str) -> dict:
ts = request.headers["X-Kunavo-Webhook-Timestamp"]
sig = request.headers["X-Kunavo-Webhook-Signature"] # "sha256=<hex>"
raw = request.get_data(as_text=True)
expected = "sha256=" + hmac.new(
secret.encode(), f"{ts}.{raw}".encode(), hashlib.sha256
).hexdigest()
if not hmac.compare_digest(expected, sig):
abort(401)
return request.get_json()
# Event body:
# { "id": "evt_...", "object": "event",
# "type": "music.completed" | "music.failed",
# "created_at": 1700000000,
# "data": { ...the GET /v1/audio/music/jobs/{id} payload... } }HMAC-SHA256 su {timestamp}.{rawBody} e viene inviata come X-Kunavo-Webhook-Signature: sha256=<hex>. La procedura è la stessa dei webhook video, quindi un unico verificatore gestisce entrambi.Quando usare quale
| Sincrono /v1/ | Asincrono /v1/ | |
|---|---|---|
| Ideale per | Script e notebook occasionali | Produzione, dispositivi mobili, webhook |
| Rete | Mantiene la connessione aperta per 1-3 min | Invia in pochi secondi; controlla lo stato o ricevi una notifica |
| Recupero dagli errori | Perdere la risposta = perdere il risultato | Esegui di nuovo la query sull’ID in qualsiasi momento prima di expires_at |
| Webhook | — | Eventi firmati music. |
Risposta e archiviazione
I campi url (audio) e image_url (copertina) di ogni brano sono link files.kunavo.com permanenti: Kunavo archivia ogni risultato sul proprio CDN, quindi, a differenza degli URL Suno originali, non scadono mai. stream_url è il link di streaming originale di Suno (utile per la riproduzione immediata). Ogni brano compare anche in /app/assets.
output.archived è false e gli URL rimandano ai link temporanei di Suno (che scadono dopo circa 24 ore): in tal caso, ospitali nuovamente per conto tuo.