class SunoAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://api.kie.ai/api/v1';
}
async generateMusic(prompt, options = {}) {
const response = await fetch(`${this.baseUrl}/generate`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt,
customMode: options.customMode || false,
instrumental: options.instrumental || false,
model: options.model || 'V3_5',
style: options.style,
title: options.title,
negativeTags: options.negativeTags,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback'
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`Generation failed: ${result.msg}`);
}
return result.data.taskId;
}
async extendMusic(audioId, options = {}) {
const response = await fetch(`${this.baseUrl}/generate/extend`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
audioId,
defaultParamFlag: options.defaultParamFlag || false,
model: options.model || 'V3_5',
prompt: options.prompt,
style: options.style,
title: options.title,
continueAt: options.continueAt,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback'
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`Extension failed: ${result.msg}`);
}
return result.data.taskId;
}
async generateLyrics(prompt, callBackUrl) {
const response = await fetch(`${this.baseUrl}/lyrics`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
prompt,
callBackUrl
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`Lyrics generation failed: ${result.msg}`);
}
return result.data.taskId;
}
async waitForCompletion(taskId, maxWaitTime = 600000) { // 10 minutes max
const startTime = Date.now();
while (Date.now() - startTime < maxWaitTime) {
const status = await this.getTaskStatus(taskId);
if (status.status === 'SUCCESS') {
return status.response;
} else if (status.status.includes('FAILED') || status.status === 'SENSITIVE_WORD_ERROR') {
throw new Error(`Generation failed: ${status.errorMessage || status.status}`);
}
// Wait 10 seconds before next check
await new Promise(resolve => setTimeout(resolve, 10000));
}
throw new Error('Generation timeout');
}
async getTaskStatus(taskId) {
const response = await fetch(`${this.baseUrl}/generate/record-info?taskId=${taskId}`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const result = await response.json();
return result.data;
}
}
// Usage Example
async function main() {
const api = new SunoAPI('YOUR_API_KEY');
try {
// Generate music with lyrics
console.log('Starting music generation...');
const taskId = await api.generateMusic(
'A nostalgic folk song about childhood memories',
{
customMode: true,
instrumental: false,
model: 'V4_5',
style: 'Folk, Acoustic, Nostalgic',
title: 'Childhood Dreams'
}
);
// Wait for completion
console.log(`Task ID: ${taskId}. Waiting for completion...`);
const result = await api.waitForCompletion(taskId);
console.log('Music generated successfully!');
console.log('Generated tracks:');
result.sunoData.forEach((track, index) => {
console.log(`Track ${index + 1}:`);
console.log(` Title: ${track.title}`);
console.log(` Audio URL: ${track.audioUrl}`);
console.log(` Duration: ${track.duration}s`);
console.log(` Tags: ${track.tags}`);
});
// Extend the first track
const firstTrack = result.sunoData[0];
console.log('\nExtending the first track...');
const extendTaskId = await api.extendMusic(firstTrack.id, {
defaultParamFlag: true,
prompt: 'Continue with a hopeful chorus',
style: 'Folk, Uplifting',
title: 'Childhood Dreams Extended',
continueAt: 60,
model: 'V4_5'
});
const extendResult = await api.waitForCompletion(extendTaskId);
console.log('Music extended successfully!');
console.log('Extended track URL:', extendResult.sunoData[0].audioUrl);
} catch (error) {
console.error('Error:', error.message);
}
}
main();