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 (!response.ok || result.code !== 200) {
throw new Error(`Generation failed: ${result.msg || 'Unknown error'}`);
}
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 (!response.ok || result.code !== 200) {
throw new Error(`Extension failed: ${result.msg || 'Unknown error'}`);
}
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 (!response.ok || result.code !== 200) {
throw new Error(`Lyrics generation failed: ${result.msg || 'Unknown error'}`);
}
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);
switch (status.status) {
case 'SUCCESS':
console.log('All tracks generated successfully!');
return status.response;
case 'FIRST_SUCCESS':
console.log('First track generation completed!');
return status.response;
case 'TEXT_SUCCESS':
console.log('Lyrics/text generation successful!');
return status.response;
case 'PENDING':
console.log('Task is pending...');
break;
case 'CREATE_TASK_FAILED':
const createError = status.errorMessage || 'Task creation failed';
console.error('Error message:', createError);
throw new Error(createError);
case 'GENERATE_AUDIO_FAILED':
const audioError = status.errorMessage || 'Audio generation failed';
console.error('Error message:', audioError);
throw new Error(audioError);
case 'CALLBACK_EXCEPTION':
const callbackError = status.errorMessage || 'Callback process error';
console.error('Error message:', callbackError);
throw new Error(callbackError);
case 'SENSITIVE_WORD_ERROR':
const sensitiveError = status.errorMessage || 'Content filtered due to sensitive words';
console.error('Error message:', sensitiveError);
throw new Error(sensitiveError);
default:
console.log(`Unknown status: ${status.status}`);
if (status.errorMessage) {
console.error('Error message:', status.errorMessage);
}
break;
}
// 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}`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const result = await response.json();
if (!response.ok || result.code !== 200) {
throw new Error(`Status check failed: ${result.msg || 'Unknown error'}`);
}
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();