Développeurs
L'API Valiha
Envoyez un enregistrement et ses paroles depuis votre application. Valiha renvoie la tonalité, le tempo, les parties, les accords sur chaque temps, une grille ChordPro, les pistes séparées et la mélodie.
Pour les organisations en formule Plus ou partenaire. Les propriétaires et administrateurs créent les clés dans Organisation, API.
URL de basehttps://valiha.marindrano.com/v1
Des exemples en JavaScript et en Python, et tous les formats de données, sont dans la documentation. Lire le guide de l'API
Authentification
Chaque requête porte une clé d'API dans l'en-tête Authorization, et s'exécute dans l'organisation à qui appartient la clé.
Authorization: Bearer vk_live_...La clé complète n'est affichée qu'une fois, à sa création. Gardez-la sur votre serveur, jamais dans une application ou une page web. Une clé révoquée cesse aussitôt de fonctionner.
Créer un chant
Envoyez le titre, la langue des paroles (mg, fr ou en), les paroles et l'enregistrement. Valiha répond aussitôt avec le nouveau chant et lance l'analyse.
Avec un lien vers l'enregistrement
Valiha télécharge le fichier lui-même : https uniquement, jusqu'à 80 Mo, en 60 secondes au plus.
curl -X POST https://valiha.marindrano.com/v1/songs \
-H "Authorization: Bearer $VALIHA_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "Antsoantso",
"artist": "Rija",
"language": "mg",
"lyrics": "Tompo ô, Ianao no hirako\nNy fitiavanao no tanjako",
"audioUrl": "https://example.com/antsoantso.mp3",
"webhookUrl": "https://example.com/hooks/valiha",
"externalId": "song-1042",
"options": { "stems": 6, "melody": ["game", "choir"] }
}'Avec le fichier
Envoyez un formulaire multipart : l'enregistrement dans un champ fichier nommé audio, les autres champs à côté.
curl -X POST https://valiha.marindrano.com/v1/songs \
-H "Authorization: Bearer $VALIHA_KEY" \
-F "title=Antsoantso" \
-F "language=mg" \
-F "lyrics=<lyrics.txt" \
-F "audio=@antsoantso.mp3" \
-F "webhookUrl=https://example.com/hooks/valiha" \
-F "externalId=song-1042" \
-F 'options={"stems": 6, "melody": ["game", "choir"]}'webhookUrl et externalId sont facultatifs. externalId est votre propre identifiant du chant, pour le retrouver plus tard.
Pistes et notes
Ajoutez options pour choisir ce que l'analyse produit. stems vaut 2, 4 ou 6 pistes : 2 donne la voix et la musique, 4 donne la voix, la batterie, la basse et les instruments, 6 ajoute la guitare et le piano. melody liste les modèles de notes : game écrit la mélodie principale, choir écrit toutes les voix qu'il entend (expérimental). Sans options, Valiha fait 4 pistes et la mélodie principale.
Dans un formulaire multipart, envoyez options dans un seul champ qui contient le même JSON.
HTTP/1.1 202 Accepted
{
"id": "cm3k2x8f40001",
"externalId": "song-1042",
"status": "queued",
"options": { "stems": 6, "melody": ["game", "choir"] },
"createdAt": "2026-10-27T09:12:00.000Z"
}Récupérer la grille
Une analyse prend quelques minutes. Demandez le chant de temps en temps, ou donnez un webhookUrl et Valiha vous appelle quand c'est fini.
La demander
Demandez toutes les 10 secondes environ, jusqu'à ce que l'état soit ready ou failed. Vous pouvez aussi retrouver vos chants par leur externalId.
curl https://valiha.marindrano.com/v1/songs/cm3k2x8f40001 \
-H "Authorization: Bearer $VALIHA_KEY"curl "https://valiha.marindrano.com/v1/songs?externalId=song-1042" \
-H "Authorization: Bearer $VALIHA_KEY"Analyser à nouveau
Envoyez options pour changer les pistes ou les notes, et elles deviennent celles du chant. Sans corps, le chant garde ses options. La grille que vous avez reste jusqu'à ce que la nouvelle soit prête.
curl -X POST https://valiha.marindrano.com/v1/songs/cm3k2x8f40001/analyze \
-H "Authorization: Bearer $VALIHA_KEY" \
-H "Content-Type: application/json" \
-d '{ "options": { "stems": 2, "melody": ["game"] } }'Télécharger une piste
stems liste les pistes de l'analyse en cours. Téléchargez chacune avec votre clé. Une piste est de l'audio AAC dans un fichier .m4a, et les requêtes Range fonctionnent.
curl https://valiha.marindrano.com/v1/songs/cm3k2x8f40001/stems/vocals \
-H "Authorization: Bearer $VALIHA_KEY" \
-o vocals.m4aToutes les routes
| Méthode | Chemin | Ce qu'elle fait |
|---|---|---|
| POST | /v1/songs | Crée un chant et lance son analyse. Répond 202. |
| GET | /v1/songs/{id} | Un chant, sous la forme décrite plus bas. |
| GET | /v1/songs?externalId=... | Vos chants avec cet externalId, du plus récent au plus ancien. |
| GET | /v1/songs/{id}/chordpro | La grille en texte ChordPro. 404 tant que le chant n'est pas prêt. |
| GET | /v1/songs/{id}/stems/{name} | Une piste de l'analyse en cours, en audio AAC (.m4a). 404 quand le chant n'a pas cette piste. |
| POST | /v1/songs/{id}/analyze | Analyse à nouveau le chant, avec de nouvelles options si vous en envoyez. Répond 202. |
| DELETE | /v1/songs/{id} | Supprime le chant et son enregistrement. Répond 204. |
Webhooks
Quand une analyse se termine, Valiha envoie le chant à son webhookUrl, avec l'événement song.ready ou song.failed.
POST https://example.com/hooks/valiha
X-Valiha-Event: song.ready
X-Valiha-Timestamp: 1793092532
X-Valiha-Signature: sha256=5f0c9a...
Content-Type: application/json
{ "event": "song.ready", "song": { "id": "cm3k2x8f40001", "status": "ready", ... } }Répondez par n'importe quel code 2xx en moins de 10 secondes. Sinon, Valiha réessaie après 30 secondes, puis après 5 minutes, puis s'arrête.
Vérifier la signature
Chaque clé est livrée avec un secret de webhook, affiché une seule fois avec la clé. La signature est un HMAC SHA-256 de l'horodatage, d'un point et du corps brut, calculé avec ce secret. Comparez-la en temps constant, et refusez les horodatages de plus de cinq minutes.
import { createHmac, timingSafeEqual } from "node:crypto";
export function isFromValiha(rawBody, headers, webhookSecret) {
const timestamp = headers["x-valiha-timestamp"];
const signature = headers["x-valiha-signature"] ?? "";
const age = Math.abs(Date.now() / 1000 - Number(timestamp));
if (!timestamp || !(age < 300)) return false;
const expected =
"sha256=" +
createHmac("sha256", webhookSecret)
.update(`${timestamp}.${rawBody}`)
.digest("hex");
const a = Buffer.from(signature);
const b = Buffer.from(expected);
return a.length === b.length && timingSafeEqual(a, b);
}L'objet chant
Toutes les routes et tous les webhooks donnent les chants sous cette forme. chart reste null tant que le chant n'est pas prêt. Ses parties et ses accords portent des temps en secondes, pour placer les accords sans lire le texte ChordPro.
{
"id": "cm3k2x8f40001",
"externalId": "song-1042",
"status": "ready",
"title": "Antsoantso",
"artist": "Rija",
"language": "mg",
"key": "G",
"bpm": 91,
"timeSignature": "4/4",
"durationSeconds": 212.4,
"error": null,
"chart": {
"chordpro": "{title: Antsoantso}\n{key: G}\n...",
"sections": [
{ "kind": "verse", "label": "Andininy 1", "start": 8.2, "end": 41.6 }
],
"chords": [
{ "time": 8.21, "beat": 12, "name": "G" },
{ "time": 10.84, "beat": 16, "name": "D" }
]
},
"options": { "stems": 6, "melody": ["game", "choir"] },
"stems": ["vocals", "drums", "bass", "guitar", "piano", "other"],
"melody": {
"version": 1,
"model": "game",
"engine": { "version": "0.1.0" },
"sha1": "9b1c4e...",
"tuning": -12.5,
"notes": [
{ "start": 8.31, "end": 8.74, "midi": 67, "beat": 12, "length": 1, "conf": 1, "syllable": [0, 0, 0, 0] }
],
"timings": { "notes": 0.41 }
},
"voices": {
"version": 1,
"model": "choir",
"sha1": "9b1c4e...",
"tuning": -12.5,
"notes": [
{ "start": 8.3, "end": 8.76, "midi": 67, "velocity": 0.81 },
{ "start": 8.3, "end": 8.76, "midi": 59, "velocity": 0.44 }
],
"timings": { "features": 3.2, "model": 18.7 }
},
"createdAt": "2026-10-27T09:12:00.000Z",
"updatedAt": "2026-10-27T09:15:32.000Z"
}options dit ce que l'analyse doit produire. stems, melody et voices viennent de l'analyse en cours : les pistes à télécharger, la mélodie principale (melody.json) et toutes les voix (voices.json). melody et voices valent null quand l'analyse n'en a pas.
status vaut queued, analyzing, ready ou failed. En cas d'échec, error en donne la raison.
Erreurs
Les erreurs reviennent en JSON, avec un code pour votre programme et un message pour les personnes.
HTTP/1.1 404 Not Found
{ "error": { "code": "not_found", "message": "This song does not exist" } }| Code | Statut | Signification |
|---|---|---|
| unauthorized | 401 | La clé manque, est fausse ou révoquée. |
| not_found | 404 | Ce chant n'existe pas dans votre organisation. |
| invalid | 400 | Un champ manque ou est faux. Le message dit lequel. |
| limit | 429 | Trop de requêtes, ou une limite de votre formule. |
| server | 500 | Un problème de notre côté. Réessayez un peu plus tard. |
Limite de débit
Chaque clé peut faire 60 requêtes par minute. Au-delà, Valiha répond 429 avec un en-tête Retry-After, en secondes.