欢迎使用 Suno API
Suno API 让您能够使用最先进的AI模型创建高质量的AI生成音乐、歌词和音频内容。无论您是在构建音乐应用、自动化创意工作流程,还是开发音频内容,我们的API都为音乐生成和音频处理提供了全面的工具。生成音乐
创建带或不带歌词的原创音乐曲目
延长音乐
无缝延长现有音乐曲目
生成歌词
从文本提示创建创意歌词
音乐视频
将音频轨道转换为可视化音乐视频
上传翻唱
将上传的音频转换为新风格
上传扩展
上传音频文件并无缝扩展
人声分离
从音乐中分离人声和伴奏
WAV转换
将音频转换为高质量WAV格式
获取歌词
获取带时间戳的同步歌词
添加伴奏
为现有音频轨道添加伴奏元素
添加人声
为器乐音乐生成人声轨道
增强音乐风格
使用V4_5对话式提示词增强风格描述
身份验证
所有 API 请求都需要使用 Bearer 令牌进行身份验证。请从 API 密钥管理页面 获取您的 API 密钥。请妥善保管您的 API 密钥,切勿公开分享。如果怀疑密钥泄露,请立即重置。
API 基础 URL
复制
https://apibox.erweima.ai
身份验证请求头
复制
Authorization: Bearer YOUR_API_KEY
快速开始指南
第一步:生成您的第一个音乐曲目
从一个简单的音乐生成请求开始:复制
curl -X POST "https://apibox.erweima.ai/api/v1/generate" \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "一首平静舒缓的钢琴曲,带有柔和的旋律",
"customMode": false,
"instrumental": true,
"model": "V4_5ALL",
"callBackUrl": "https://your-app.com/callback",
"vocalGender": "m",
"styleWeight": 0.65,
"weirdnessConstraint": 0.3,
"audioWeight": 0.7
}'
第二步:检查任务状态
使用返回的任务ID检查生成状态:复制
curl -X GET "https://apibox.erweima.ai/api/v1/generate/record-info?taskId=YOUR_TASK_ID" \
-H "Authorization: Bearer YOUR_API_KEY"
响应格式
成功响应:复制
{
"code": 200,
"msg": "success",
"data": {
"taskId": "5c79****be8e"
}
}
复制
{
"code": 200,
"msg": "success",
"data": {
"taskId": "5c79****be8e",
"status": "SUCCESS",
"response": {
"data": [
{
"id": "8551****662c",
"audio_url": "https://example.cn/****.mp3",
"stream_audio_url": "https://example.cn/****",
"image_url": "https://example.cn/****.jpeg",
"prompt": "一首平静舒缓的钢琴曲",
"title": "宁静钢琴",
"tags": "平静, 舒缓, 钢琴",
"duration": 198.44,
"createTime": "2025-01-01 00:00:00"
}
]
}
}
}
核心功能
- 文本转音乐:输入文字描述,生成相应的音乐作品
- 延长音乐:基于现有音频,无缝创建更长版本
- 生成歌词:从创意提示生成结构化歌词内容
- 上传翻唱:上传音频文件,转换为不同的音乐风格
- 上传扩展:上传音频文件并在保持风格的同时无缝扩展
- 人声分离:将音乐分离为人声、伴奏等独立轨道,支持高级干声分离
- 格式转换:支持WAV等多种高质量音频格式输出
- 音乐视频:将音频转换为视觉化的音乐视频
- 添加人声:为现有的器乐音乐生成人声轨道
- 添加伴奏:为人声轨道创建器乐伴奏
- 风格增强:使用V4_5+对话式提示词提升和优化现有音乐的风格特色
- 带时间戳歌词:获取同步歌词用于卡拉OK式应用
AI 模型
为您的需求选择合适的模型:V4_5ALL
扩展功能支持更长的歌词(5000字符)和风格描述(1000字符)
V4
改进的人声最长4分钟,增强的人声质量
V4_5
更好的歌曲结构最长1分钟,改进的歌曲组织
V4_5PLUS
更丰富的音色最长8分钟,新的创作方式
V5
最新模型增强的质量和功能
生成模式
控制参数复杂度:
false: 简单模式,仅需要提示词true: 高级模式,需要风格和标题
决定音乐是否包含人声:
true: 仅纯音乐(无歌词)false: 包含人声/歌词
关键参数
对所需音乐的文本描述。请具体说明流派、情绪和乐器。字符限制:
- 非自定义模式:500字符
- 自定义模式(V4):3000字符
- 自定义模式(V4_5, V4_5PLUS, V5 & V4_5ALL):5000字符
音乐风格规范(仅自定义模式)。示例: 爵士、古典、电子、流行、摇滚、嘻哈字符限制:
- V4:200字符
- V4_5, V4_5PLUS, V5 & V4_5ALL:1000字符
生成音乐曲目的标题(仅自定义模式)。最大长度: 80字符
完整工作流程示例
以下是一个生成带歌词音乐并等待完成的完整示例:- JavaScript
- Python
复制
class SunoAPI {
constructor(apiKey) {
this.apiKey = apiKey;
this.baseUrl = 'https://apibox.erweima.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 || 'V4_5ALL',
style: options.style,
title: options.title,
negativeTags: options.negativeTags,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback',
vocalGender: options.vocalGender,
styleWeight: options.styleWeight,
weirdnessConstraint: options.weirdnessConstraint,
audioWeight: options.audioWeight
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`生成失败: ${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 || 'V4_5ALL',
prompt: options.prompt,
style: options.style,
title: options.title,
continueAt: options.continueAt,
callBackUrl: options.callBackUrl || 'https://your-app.com/callback',
vocalGender: options.vocalGender,
styleWeight: options.styleWeight,
weirdnessConstraint: options.weirdnessConstraint,
audioWeight: options.audioWeight
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`延长失败: ${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(`歌词生成失败: ${result.msg}`);
}
return result.data.taskId;
}
async addVocals(uploadUrl, prompt, style, title, negativeTags, options = {}) {
const response = await fetch(`${this.baseUrl}/generate/add-vocals`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
uploadUrl,
prompt,
style,
title,
negativeTags,
model: options.model || 'V4_5PLUS',
callBackUrl: options.callBackUrl || 'https://your-app.com/callback',
vocalGender: options.vocalGender,
styleWeight: options.styleWeight,
weirdnessConstraint: options.weirdnessConstraint,
audioWeight: options.audioWeight
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`添加人声失败: ${result.msg}`);
}
return result.data.taskId;
}
async addInstrumental(uploadUrl, title, tags, negativeTags, options = {}) {
const response = await fetch(`${this.baseUrl}/generate/add-instrumental`, {
method: 'POST',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
uploadUrl,
title,
tags,
negativeTags,
model: options.model || 'V4_5PLUS',
callBackUrl: options.callBackUrl || 'https://your-app.com/callback',
vocalGender: options.vocalGender,
styleWeight: options.styleWeight,
weirdnessConstraint: options.weirdnessConstraint,
audioWeight: options.audioWeight
})
});
const result = await response.json();
if (result.code !== 200) {
throw new Error(`添加伴奏失败: ${result.msg}`);
}
return result.data.taskId;
}
async waitForCompletion(taskId, maxWaitTime = 600000) { // 最长等待10分钟
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(`生成失败: ${status.errorMessage || status.status}`);
}
// 等待10秒后再次检查
await new Promise(resolve => setTimeout(resolve, 10000));
}
throw new Error('生成超时');
}
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;
}
async getRemainingCredits() {
const response = await fetch(`${this.baseUrl}/generate/credit`, {
headers: {
'Authorization': `Bearer ${this.apiKey}`
}
});
const result = await response.json();
return result.data.credits;
}
}
// 使用示例
async function main() {
const api = new SunoAPI('YOUR_API_KEY');
try {
// 检查剩余积分
const credits = await api.getRemainingCredits();
console.log(`剩余积分: ${credits}`);
// 生成带歌词的音乐
console.log('开始生成音乐...');
const taskId = await api.generateMusic(
'一首关于童年回忆的怀旧民谣',
{
customMode: true,
instrumental: false,
model: 'V4_5',
style: '民谣, 原声吉他, 怀旧',
title: '童年梦想'
}
);
// 等待完成
console.log(`任务ID: ${taskId}。等待完成...`);
const result = await api.waitForCompletion(taskId);
console.log('音乐生成成功!');
console.log('生成的曲目:');
result.data.forEach((track, index) => {
console.log(`曲目 ${index + 1}:`);
console.log(` 标题: ${track.title}`);
console.log(` 音频URL: ${track.audio_url}`);
console.log(` 时长: ${track.duration}秒`);
console.log(` 标签: ${track.tags}`);
});
// 延长第一个曲目
const firstTrack = result.data[0];
console.log('\n延长第一个曲目...');
const extendTaskId = await api.extendMusic(firstTrack.id, {
defaultParamFlag: true,
prompt: '继续一个充满希望的副歌',
style: '民谣, 振奋',
title: '童年梦想延长版',
continueAt: 60,
model: 'V4_5'
});
const extendResult = await api.waitForCompletion(extendTaskId);
console.log('音乐延长成功!');
console.log('延长曲目URL:', extendResult.data[0].audio_url);
} catch (error) {
console.error('错误:', error.message);
}
}
main();
复制
import requests
import time
class SunoAPI:
def __init__(self, api_key):
self.api_key = api_key
self.base_url = 'https://apibox.erweima.ai/api/v1'
self.headers = {
'Authorization': f'Bearer {api_key}',
'Content-Type': 'application/json'
}
def generate_music(self, prompt, **options):
data = {
'prompt': prompt,
'customMode': options.get('customMode', False),
'instrumental': options.get('instrumental', False),
'model': options.get('model', 'V4_5ALL'),
'callBackUrl': options.get('callBackUrl', 'https://your-app.com/callback')
}
if options.get('style'):
data['style'] = options['style']
if options.get('title'):
data['title'] = options['title']
if options.get('negativeTags'):
data['negativeTags'] = options['negativeTags']
if options.get('vocalGender'):
data['vocalGender'] = options['vocalGender']
if options.get('styleWeight'):
data['styleWeight'] = options['styleWeight']
if options.get('weirdnessConstraint'):
data['weirdnessConstraint'] = options['weirdnessConstraint']
if options.get('audioWeight'):
data['audioWeight'] = options['audioWeight']
response = requests.post(f'{self.base_url}/generate',
headers=self.headers, json=data)
result = response.json()
if result['code'] != 200:
raise Exception(f"生成失败: {result['msg']}")
return result['data']['taskId']
def extend_music(self, audio_id, **options):
data = {
'audioId': audio_id,
'defaultParamFlag': options.get('defaultParamFlag', False),
'model': options.get('model', 'V4_5ALL'),
'callBackUrl': options.get('callBackUrl', 'https://your-app.com/callback')
}
if options.get('prompt'):
data['prompt'] = options['prompt']
if options.get('style'):
data['style'] = options['style']
if options.get('title'):
data['title'] = options['title']
if options.get('continueAt'):
data['continueAt'] = options['continueAt']
if options.get('vocalGender'):
data['vocalGender'] = options['vocalGender']
if options.get('styleWeight'):
data['styleWeight'] = options['styleWeight']
if options.get('weirdnessConstraint'):
data['weirdnessConstraint'] = options['weirdnessConstraint']
if options.get('audioWeight'):
data['audioWeight'] = options['audioWeight']
response = requests.post(f'{self.base_url}/generate/extend',
headers=self.headers, json=data)
result = response.json()
if result['code'] != 200:
raise Exception(f"延长失败: {result['msg']}")
return result['data']['taskId']
def generate_lyrics(self, prompt, callback_url):
data = {
'prompt': prompt,
'callBackUrl': callback_url
}
response = requests.post(f'{self.base_url}/lyrics',
headers=self.headers, json=data)
result = response.json()
if result['code'] != 200:
raise Exception(f"歌词生成失败: {result['msg']}")
return result['data']['taskId']
def add_vocals(self, upload_url, prompt, style, title, negative_tags, **options):
data = {
'uploadUrl': upload_url,
'prompt': prompt,
'style': style,
'title': title,
'negativeTags': negative_tags,
'model': options.get('model', 'V4_5PLUS'),
'callBackUrl': options.get('callBackUrl', 'https://your-app.com/callback')
}
if options.get('vocalGender'):
data['vocalGender'] = options['vocalGender']
if options.get('styleWeight'):
data['styleWeight'] = options['styleWeight']
if options.get('weirdnessConstraint'):
data['weirdnessConstraint'] = options['weirdnessConstraint']
if options.get('audioWeight'):
data['audioWeight'] = options['audioWeight']
response = requests.post(f'{self.base_url}/generate/add-vocals',
headers=self.headers, json=data)
result = response.json()
if result['code'] != 200:
raise Exception(f"添加人声失败: {result['msg']}")
return result['data']['taskId']
def add_instrumental(self, upload_url, title, tags, negative_tags, **options):
data = {
'uploadUrl': upload_url,
'title': title,
'tags': tags,
'negativeTags': negative_tags,
'model': options.get('model', 'V4_5PLUS'),
'callBackUrl': options.get('callBackUrl', 'https://your-app.com/callback')
}
if options.get('vocalGender'):
data['vocalGender'] = options['vocalGender']
if options.get('styleWeight'):
data['styleWeight'] = options['styleWeight']
if options.get('weirdnessConstraint'):
data['weirdnessConstraint'] = options['weirdnessConstraint']
if options.get('audioWeight'):
data['audioWeight'] = options['audioWeight']
response = requests.post(f'{self.base_url}/generate/add-instrumental',
headers=self.headers, json=data)
result = response.json()
if result['code'] != 200:
raise Exception(f"添加伴奏失败: {result['msg']}")
return result['data']['taskId']
def wait_for_completion(self, task_id, max_wait_time=600):
start_time = time.time()
while time.time() - start_time < max_wait_time:
status = self.get_task_status(task_id)
if status['status'] == 'SUCCESS':
return status['response']
elif 'FAILED' in status['status'] or status['status'] == 'SENSITIVE_WORD_ERROR':
error_msg = status.get('errorMessage', status['status'])
raise Exception(f"生成失败: {error_msg}")
time.sleep(10) # 等待10秒
raise Exception('生成超时')
def get_task_status(self, task_id):
response = requests.get(f'{self.base_url}/generate/record-info?taskId={task_id}',
headers={'Authorization': f'Bearer {self.api_key}'})
return response.json()['data']
def get_remaining_credits(self):
response = requests.get(f'{self.base_url}/generate/credit',
headers={'Authorization': f'Bearer {self.api_key}'})
return response.json()['data']['credits']
# 使用示例
def main():
api = SunoAPI('YOUR_API_KEY')
try:
# 检查剩余积分
credits = api.get_remaining_credits()
print(f'剩余积分: {credits}')
# 生成带歌词的音乐
print('开始生成音乐...')
task_id = api.generate_music(
'一首关于童年回忆的怀旧民谣',
customMode=True,
instrumental=False,
model='V4_5',
style='民谣, 原声吉他, 怀旧',
title='童年梦想'
)
# 等待完成
print(f'任务ID: {task_id}。等待完成...')
result = api.wait_for_completion(task_id)
print('音乐生成成功!')
print('生成的曲目:')
for i, track in enumerate(result['data']):
print(f"曲目 {i + 1}:")
print(f" 标题: {track['title']}")
print(f" 音频URL: {track['audio_url']}")
print(f" 时长: {track['duration']}秒")
print(f" 标签: {track['tags']}")
# 延长第一个曲目
first_track = result['data'][0]
print('\n延长第一个曲目...')
extend_task_id = api.extend_music(
first_track['id'],
defaultParamFlag=True,
prompt='继续一个充满希望的副歌',
style='民谣, 振奋',
title='童年梦想延长版',
continueAt=60,
model='V4_5'
)
extend_result = api.wait_for_completion(extend_task_id)
print('音乐延长成功!')
print(f"延长曲目URL: {extend_result['data'][0]['audio_url']}")
except Exception as error:
print(f'错误: {error}')
if __name__ == '__main__':
main()
状态码和任务状态
任务正在等待处理或正在生成中
歌词/文本生成成功完成
第一个曲目生成完成
所有曲目生成成功
创建任务失败
生成音频失败
内容因敏感词被过滤
HTTP 状态码
请求成功
请求参数错误或缺失
没有访问权限,检查API密钥
请求方式或路径错误
调用超过限制
提示词或主题过长
账户积分不足
网站维护中
服务器内部错误
最佳实践
提示词工程
提示词工程
- 具体说明流派、情绪和乐器
- 使用描述性形容词获得更好的风格控制
- 包含节拍和能量水平描述
- 参考音乐时代或特定艺术家进行风格指导
模型选择
模型选择
- V4_5ALL:最适合需要更长歌词和风格描述的项目
- V4:当人声质量最重要时选择
- V4_5:用于更好的歌曲结构,最长1分钟
- V4_5PLUS:选择最高质量和最长的曲目
性能优化
性能优化
- 使用回调而不是频繁轮询
- 从非自定义模式开始满足简单需求
- 实施适当的错误处理以应对生成失败
- 缓存生成内容,因为文件14-15天后到期
内容指南
内容指南
- 避免在提示中使用受版权保护的材料
- 使用原创歌词和音乐描述
- 注意歌词内容的内容政策
- 测试提示变化以避免敏感词过滤器
错误处理
内容政策违反(代码 400)
内容政策违反(代码 400)
复制
try {
const taskId = await api.generateMusic('受版权保护的歌词');
} catch (error) {
if (error.data.code === 400) {
console.log('请仅使用原创内容');
}
}
积分不足(代码 429)
积分不足(代码 429)
复制
try {
const taskId = await api.generateMusic('原创作品');
} catch (error) {
if (error.data.code === 429) {
console.log('请为您的账户添加更多积分');
}
}
速率限制(代码 405)
速率限制(代码 405)
复制
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function generateWithRetry(prompt, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await api.generateMusic(prompt, options);
} catch (error) {
if (error.data.code === 405 && i < maxRetries - 1) {
await delay(Math.pow(2, i) * 1000); // 指数退避
continue;
}
throw error;
}
}
}
支持
需要帮助吗?我们的技术支持团队随时为您提供帮助。
- 邮箱: [email protected]
- 文档: 查看详细的API文档和示例
- API状态: 查看我们的状态页面了解实时API健康状况
准备开始创作令人惊叹的AI音乐了吗?获取您的API密钥,立即开始创作!
