Documentation
Génération musicale
Génération de musique à partir de texte avec Suno V5 / V5.5 — voix, paroles et contrôle du style. Deux formats d’API : un point de terminaison synchrone unique qui attend que les pistes soient prêtes, et une paire asynchrone de soumission et d’interrogation (avec webhooks signés) pour la production.
Points de terminaison : POST /v1/audio/music (synchrone, présenté en premier) et POST /v1/audio/music/jobs + GET /v1/audio/music/jobs/{id} (asynchrone, recommandé pour la production). Chaque requête génère deux pistes ; la génération dure environ 1–3 minutes. Prix : $0.09 par demande de génération sur suno-v5, $0.09 sur suno-v5-5 — les deux pistes sont facturées une seule fois, depuis un solde prépayé, et une génération échouée n’est pas facturée.
Démarrage rapide (synchrone)
Transmettez un prompt et un modèle Suno. La requête reste ouverte jusqu’à ce que les deux pistes soient prêtes, puis renvoie leurs URL permanentes.
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"))Paramètres
| Paramètre | Type | Remarques |
|---|---|---|
model | chaîne (obligatoire) | Un slug de modèle musical — suno-v5 ou suno-v5-5. |
prompt | chaîne (obligatoire) | Description de la musique (ou paroles en mode personnalisé). |
instrumental | bool | true = sans voix. Valeur par défaut : false. |
customMode | bool | Mode Suno avancé — contrôlez explicitement la structure, les paroles et le style. |
style | string | Indication de style ou de genre, par exemple « lofi, jazz, chill ». |
title | string | Titre de la piste. |
webhook_url | chaîne (https) | Mode asynchrone uniquement — envoyez ici un événement terminal signé. Voir Webhooks. |
Modèles musicaux
| Slug | Fournisseur | Remarques |
|---|---|---|
suno-v5 | Suno | Suno V5 — voix, paroles et fusion de styles. |
suno-v5-5 | Suno | Suno V5.5 — le plus récent, avec une fidélité supérieure et des générations plus longues. |
Consultez /models pour obtenir la liste à jour et les tarifs par appel.
API asynchrone (soumettre + interroger)
Points de terminaison : POST /v1/audio/music/jobs + GET /v1/audio/music/jobs/{id}. Reprend le fonctionnement de l’API vidéo asynchrone ; le même code d’interrogation et de webhook fonctionne donc pour les deux modalités.
Le point de terminaison synchrone maintient une connexion HTTP ouverte pendant plusieurs minutes — pratique pour les scripts, mais peu fiable sur mobile. La paire asynchrone renvoie immédiatement un identifiant msc_* ; interrogez GET /v1/audio/music/jobs/{id} jusqu’à ce que status soit completed ou failed. Mêmes modèles, mêmes tarifs et mêmes URL CDN permanentes.
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"])Structure de la réponse
| Champ | Type | Remarques |
|---|---|---|
id | string | Identifiant de tâche préfixé par msc_. |
object | string | "music". |
status | string | en attente | en cours | terminée | échouée. |
model | string | Slug du modèle envoyé. |
created_at | int | Horodatage Unix (secondes) de l’acceptation de la soumission. |
completed_at | int | null | Horodatage Unix (secondes) de la fin de la tâche ; null tant qu’elle est en attente. |
expires_at | int | Horodatage Unix (secondes) après lequel le résultat est purgé (environ 30 jours). |
progress | int | 0 jusqu’à la fin, puis 100. |
output | object | null | {tracks[], archived} une fois terminé. |
output. | array | Chaque élément : {url, stream_url, image_url}. url / image_url sont des liens CDN permanents. |
error | object | null | {code, message} en cas d’échec. |
En-têtes et conventions
Idempotency-Key(facultatif, ≤128 caractères) — soumettre une deuxième fois avec la même clé sur le même compte renvoie la tâche d’origine au lieu d’en créer un doublon.POST /v1/audio/music/jobsrenvoie 202 Accepted lors d’une nouvelle soumission et 200 OK lorsqu’une clé d’idempotence rejoue un résultat.GETest limité au propriétaire : interroger une tâche appartenant à un autre compte renvoie 404.- Fréquence d’interrogation recommandée : 5 s, avec un délai progressif jusqu’à 30 s.
Webhooks
Transmettez webhook_url (https) lors de la soumission ; Kunavo envoie alors par POST un événement terminal signé music.completed / music.failed dès que la tâche se termine, avec des tentatives répétées et un délai progressif. Le champ data de l’événement correspond exactement à la charge utile GET ci-dessus.
# 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 sur {timestamp}.{rawBody}, envoyée dans X-Kunavo-Webhook-Signature: sha256=<hex>. Même méthode que pour les webhooks vidéo : un seul vérificateur suffit pour les deux.Quel format choisir ?
| Synchrone /v1/ | Asynchrone /v1/ | |
|---|---|---|
| Idéal pour | Scripts ponctuels, notebooks | Production, mobile, webhooks |
| Réseau | Maintient la connexion ouverte pendant 1-3 min | Envoyez en quelques secondes ; interrogez ou recevez une notification |
| Récupération après échec | Perdre la réponse = perdre le résultat | Réinterrogez l’identifiant à tout moment avant expires_at |
| Webhooks | — | Événements signés music. |
Réponse et stockage
Le url (audio) et le image_url (illustration de couverture) de chaque piste sont des liens permanents files.kunavo.com — Kunavo archive chaque résultat sur son propre CDN ; contrairement aux URL Suno brutes, ils n’expirent donc jamais. stream_url est le lien de diffusion en continu original de Suno (pratique pour une lecture immédiate). Chaque piste apparaît également dans /app/assets.
output.archived vaut false et les URL renvoient aux liens temporaires de Suno (qui expirent au bout d’environ 24 h) ; dans ce cas, hébergez-les vous-même.