【권장】 음색 클론 API
✅ 새 연동에는 이 경로를 권장합니다. 다수의 사용자가 실제로 평가한 결과, 이 경로로 생성한 곡은 원음 재현도가 높아 완성품의 보컬이 업로드한 샘플 속 본인 음색에 더 가깝습니다. 그래서 현재 대외적으로 우선 추천하는 경로입니다.
비교: 초기의 보이스 클론 및 지정 음색 생성 API(권장하지 않음)도 여전히 사용할 수 있지만, 내비게이션에 "권장하지 않음"으로 표시되어 있으며 새 연동에는 권장하지 않습니다.
- 문서 날짜: 2026-09-26
- 지원 범위: 내 음색 만들기 → 그 음색으로 지정한 곡 커버 / 음색으로 신곡 부르기 → 진행 상황 조회 → 완성품 다운로드
- 본문의 모든 인터페이스, 필드, 응답은 실제 호출로 검증했습니다("실측"이라고 표시한 항목이 중점 검증 대상)
설명: 이 문서는 호출해야 하는 인터페이스만 다룹니다. 소재 처리, 작업 스케줄링, 콘텐츠 검증 등은 서버가 자동으로 수행하므로 클라이언트에서 구현할 필요가 없습니다.
0. 5분 만에 시작하기
네 단계면 보컬 샘플 하나에서 완성곡 두 곡을 얻습니다.
① 음색 만들기 POST /api/music/voice-profile/create → voice_id 획득(status=ready)
② 음색으로 커버 POST /api/music/voice-profile/cover → 곧바로 song_id 2개 획득
③ 진행 조회 POST /api/music/query → status=completed까지 폴링
④ 완성품 다운로드 GET /api/music/download?song_id=…&kind=mp3
최소 사용 예시(curl):
API_KEY="sk-내-키"
BASE="https://www.suno-api.io"
# ① 음색 만들기(보컬 샘플 업로드, 10초 이상·10MB 이내 권장)
curl -sS -X POST "$BASE/api/music/voice-profile/create" \
-H "Authorization: Bearer $API_KEY" \
-F "file=@./my-voice.mp3" \
-F "title=내 음색" \
-F "source_kind=upload"
# → {"success":true,"message":"","data":{"voice_id":"vp_2026…","status":"ready", …}}
# ② 이 음색으로 한 곡 커버(song_clip_id = 커버할 곡의 clip id)
curl -sS -X POST "$BASE/api/music/voice-profile/cover" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Create-Ui-Client-Request-Id: cover-$(date +%s)" \
-H "Content-Type: application/json" \
-d '{
"model": "suno-v6",
"voice_id": "vp_2026…",
"song_clip_id": "5b773e9a-…",
"lyrics": "[Verse 1]\n가사 본문…",
"style_requirements": "슬로우 포크, 어쿠스틱 기타",
"audio_weight": 0.85,
"max_mode": false
}'
# → {"code":200,"message":"success","data":{"resolved_scene":"cover","clips":[{…},{…}]}}
# ③ 폴링(여러 id를 반각 쉼표로 구분해 한 번에 전달)
curl -sS -X POST "$BASE/api/music/query" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"suno-v6","song_ids":"id1,id2"}'
# ④ 완료 후 mp3 다운로드(status=completed여야 파일을 받습니다)
curl -sS -L "$BASE/api/music/download?song_id=id1&kind=mp3" \
-H "Authorization: Bearer $API_KEY" -o song1.mp3
1. 공통 규약
1.1 도메인과 환경
| 환경 | Base URL | 용도 |
|---|---|---|
| 프로덕션 | https://www.suno-api.io |
정식 고객 제공 |
아래의 모든 경로는 Base URL 기준 상대 경로입니다. 예를 들어 POST /api/music/voice-profile/create의 전체 주소는
https://www.suno-api.io/api/music/voice-profile/create입니다.
1.2 인증
모든 인터페이스가 같은 요청 헤더를 쓰고, 값은 콘솔에서 만든 API Key입니다.
Authorization: Bearer sk-xxxxxxxx
Bearer접두사는 있어도 없어도 됩니다.sk-접두사도 마찬가지입니다(Authorization: sk-xxx도 유효).- Key가 없거나 유효하지 않으면 → HTTP 401, 응답 본문은
{"success":false,"message":"…"}형태입니다(문구는 요청 언어에 따라 달라집니다). - 각 Key는 자기 계정의 곡과 음색만 볼 수 있고 조작할 수 있습니다.
1.3 요청 본문 형식
- 음색 만들기:
multipart/form-data(파일 업로드)를 권장하며application/json(URL 또는 base64)도 지원합니다. - 그 밖의 인터페이스:
application/json.
1.4 두 가지 응답 껍데기(고객이 구분하면 되는 것은 이 둘뿐)
"음색 관리" 계열(create / create-from-clip / list / detail / delete)은 업무 껍데기를 씁니다.
{ "success": true, "message": "", "data": { } }
{ "success": false, "message": "voice_id is required" }
"음색 커버"는 생성 채널을 지나므로 생성 껍데기를 씁니다. 성공은 code/message/data, 실패는 error 객체입니다.
{ "code": 200, "message": "success", "data": { } }
{ "error": { "message": "voice not found: vp_xxx", "type": "bad_response_status_code", "param": "", "code": "bad_response_status_code" } }
판단 순서: 먼저 HTTP 상태 코드, 다음 success === false, 마지막으로 code !== 200 또는 error 존재 여부를 봅니다.
업무 실패가 HTTP 비200을 보장하지 않습니다(음색 관리 계열은 오류일 때도 HTTP 200입니다). 따라서 HTTP 상태 코드만 보고 판단하면 안 됩니다.
1.5 멱등성
음색 만들기("기존 오디오에서 만들기" 포함)는 진짜 멱등입니다: 같은 Key 아래에서 같은 client_request_id를 다시 보내도 음색은 한 건만 만들어지고, 두 번째는 첫 번째 결과를 그대로 돌려줍니다(success=true, 같은 voice_id). 사용법은 클라이언트가 직접 id(UUID 등)를 만들어 두고 타임아웃 재시도 때 같은 값을 재사용하는 것입니다.
커버 제출은 요청 단위 중복 제거를 하지 않습니다(한 번의 과금 생성입니다). 두 가지를 주의하세요.
- 제출 식별자는 요청 헤더
X-Create-Ui-Client-Request-Id: <내 제출 id>에 넣습니다. 이것은 서버 측 작업 기록에 남아 사후 조사에 도움이 되지만, 중복 과금을 막지는 않습니다. - 제출 타임아웃 ≠ 실패입니다. 요청이 이미 접수되어 생성 중일 수 있습니다. 재시도 전에
GET /api/music/songs?p=1&page_size=20(페이징 파라미터는p/page_size, 호환 표기ps도 가능)으로generation_type=voice_profile_cover인 새 곡이 이미 생겼는지 확인하고, 없을 때만 같은 요청을 다시 보내세요.
create와 create-from-clip의 본문에도 client_request_id 필드가 있습니다(§3, §4 참고).
1.6 과금 개요
| 동작 | 인터페이스 | 가격 | 설명 |
|---|---|---|---|
| 음색 만들기(일반) | POST /api/music/voice-profile/create |
무료 | 샘플 업로드는 Suno 쿼터를 소모하지 않습니다 |
| 음색 만들기(기존 오디오에서) | POST /api/music/voice-profile/create-from-clip |
무료 | 서버는 소재 참조만 등록하고 다시 업로드하지 않습니다 |
| 강화 클론 | 동일, enhanced=true |
"강화 업로드 가격"(기본 10위안/회) | 비동기. 실패 시 자동 환불 |
| 음색 커버 | POST /api/music/voice-profile/cover |
1회 1.2위안, max_mode=true면 2.4위안 |
한 번에 2곡 반환 |
| 조회 / 목록 / 상세 / 삭제 | — | 무료 | |
| 다운로드(직접 링크 사용) | 응답의 audio_url |
무료 | 발급한 주소로 바로 재생 |
| 다운로드(wav 변환 / 가사 등) | POST /api/music/free2 |
성공 1곡당 0.2위안 | §8 참고 |
가격은 언제든 공개 인터페이스에서 읽을 수 있으므로 하드코딩하지 마세요(§9 참고).
curl -sS https://www.suno-api.io/api/pricing/catalog | jq '.data.catalog.meta'
# {
# "generation_price": "0.6",
# "voice_generation_price": "1.2", ← 음색 커버 기준가
# "max_mode_factor": "2",
# "enhanced_upload_price": "10", ← 강화 클론 가격
# "voice_auto_verify_price": "5",
# "voice_long_term_clone_price": "5"
# }
커버는 선과금 + 실패 시 환불 방식입니다:
- 거부된 요청(파라미터 오류, 플랫폼 4xx/5xx, 잔액 부족 자체)은 과금되지 않습니다. 존재하지 않는 음색으로 제출한 실측에서 잔액이 전혀 변하지 않았습니다(delta = 0).
- 접수된 작업이 최종 실패하면(플랫폼 서비스 이상 포함) 서버 측 환불 경로로 선과금액이 환불됩니다.
1.7 상태 사전
음색의 status:
| 값 | 의미 | 커버 사용 가능 |
|---|---|---|
ready |
준비 완료 | ✅ |
processing |
강화 클론(enhanced=true)에서만 나타납니다. 처리 중 |
❌ (ready를 기다림) |
failed |
생성 실패. error_message 확인. 강화 클론은 환불 완료 |
❌ |
곡의 status(조회 인터페이스 반환값):
| 값 | 의미 | 다운로드 가능 |
|---|---|---|
submitted / queued / processing |
생성 중(미리듣기 스트림이 이미 있을 수 있음) | ❌ |
completed |
완료 | ✅ |
failed |
실패. 비용은 환불됨 | ❌ |
2. 인터페이스 개요
| # | 메서드 | 경로 | 역할 | 과금 |
|---|---|---|---|---|
| 1 | POST | /api/music/voice-profile/create |
음색 만들기(업로드 / URL / base64) | 무료(enhanced=true 제외) |
| 2 | POST | /api/music/voice-profile/create-from-clip |
내가 가진 오디오로 음색 만들기 | 무료 |
| 3 | GET | /api/music/voice-profile/list |
음색 목록 | 무료 |
| 4 | GET | /api/music/voice-profile/{voice_id} |
음색 상세(강화 클론 진행 포함) | 무료 |
| 5 | POST | /api/music/voice-profile/delete |
음색 삭제 | 무료 |
| 6 | POST | /api/music/voice-profile/cover |
음색으로 커버 / 신곡 부르기 | 1.2위안(MAX 2.4) |
| 7 | POST | /api/music/query |
곡 상태 일괄 조회 | 무료 |
| 8 | GET | /api/music/songs/{song_id} |
곡 단건 상세 | 무료 |
| 9 | GET | /api/music/download |
완성 파일 다운로드 | 무료(mp3/재생 링크) |
| 10 | POST | /api/music/free2 |
wav 변환 / 가사 등 | 성공 1곡당 0.2위안 |
| 11 | POST | /api/music/free2/cached |
아카이브된 다운로드 주소 조회 | 무료 |
| 12 | GET | /api/music/songs |
내 작품 목록(페이징) | 무료 |
3. 음색 만들기
POST /api/music/voice-profile/create
한 구간의 보컬 오디오를 "음색"으로 등록하고 voice_id를 받습니다. 플랫폼에게 음색은 "오디오 한 구간 + 그 카탈로그 안의 clip"에 불과하므로, 생성에 성공하면 그 원본 오디오는 영구 보관됩니다(이후 커버에 사용).
3.1 세 가지 입력 형식(하나만 선택)
형식 A: multipart 파일 업로드(권장)
Content-Type: multipart/form-data
file=<오디오 바이너리> # 필수
title=<음색 이름> # 선택. 비우면 파일명에서 확장자를 뺀 값
file_name=<원본 파일명>
source_file_name=<원본 파일명>
source_kind=upload|record # 선택. 출처 표시(업로드 / 녹음), 기본 upload
enhanced=false|true # 선택. true = 강화 클론(§3.5 참고)
client_request_id=<멱등 키> # 선택
voice_id=<사용자 지정 ID> # 선택. 보통 쓰지 않음
curl -sS -X POST "https://www.suno-api.io/api/music/voice-profile/create" \
-H "Authorization: Bearer $API_KEY" \
-F "file=@./my-voice.mp3;type=audio/mpeg" \
-F "file_name=my-voice.mp3" \
-F "source_file_name=my-voice.mp3" \
-F "title=내 음색" \
-F "source_kind=upload"
형식 B: JSON + 공용 오디오 URL
curl -sS -X POST "https://www.suno-api.io/api/music/voice-profile/create" \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"file_url": "https://your-cdn.example.com/voice/my-voice.mp3",
"file_name": "my-voice.mp3",
"title": "내 음색"
}'
- URL은 공용에서 접근 가능한 HTTPS 직링크여야 하며, 서버가 한 번 내려받아 우리 오브젝트 스토리지로 옮겨 저장합니다.
- 주소를
audio_url필드에 넣어도 효과는 같습니다(둘 다 보내면file_url을 우선). - 이미 이 플랫폼의 오브젝트 스토리지 주소(
*.myqcloud.com/create-ui/voice-source/...)라면 다시 내려받지 않고 그대로 등록합니다.
형식 C: JSON + base64
{
"file_base64": "data:audio/mpeg;base64,//uQxAAA…",
"file_name": "my-voice.mp3",
"title": "내 음색"
}
data:audio/xxx;base64,접두사와 순수 base64를 모두 지원합니다.- 크기 제한이 있습니다(base64는 1.33배로 커집니다). 큰 파일은 형식 A를 쓰세요.
3.2 오디오 요건
| 항목 | 요건 | 비고 |
|---|---|---|
| 크기 | 10MB 이하 | 초과하면 게이트웨이가 본문을 잘라 "Suno가 거부함"처럼 보입니다. 먼저 압축하거나 짧게 자르세요 |
| 길이 | 최소 6초, 10~60초 권장 | 6초 미만은 플랫폼이 거부합니다: Uploaded audio is too short (currently 2.4 seconds). Minimum duration is 6 seconds. |
| 형식 | mp3 / wav / m4a / aac / flac / ogg | 파일 확장자로 판정 |
| 내용 | 깨끗한 보컬. 커버 완성품이나 반주가 있는 원곡은 피하세요 | 카탈로그의 기존 녹음과 매우 비슷하면 거부됩니다: This audio matches an existing recording in our catalog.(실측) |
3.3 응답
{
"success": true,
"message": "",
"data": {
"voice_id": "vp_20260926080618509168500DXXrxe",
"clip_id": "91cc5935-389f-4193-b56b-cc796c15562d",
"song_id": "91cc5935-389f-4193-b56b-cc796c15562d",
"account_id": "acc_1790085275308",
"title": "내 음색",
"source_file_name": "my-voice.mp3",
"audio_url": "https://suno-api-p-1437223057.cos.ap-guangzhou.myqcloud.com/create-ui/voice-source/2026/09/26/xxx.mp3",
"image_url": "https://cdn2.suno.ai/image_91cc5935-389f-4193-b56b-cc796c15562d.jpeg",
"duration_s": 29.18,
"enhanced": false,
"source_kind": "upload",
"status": "ready",
"client_request_id": "",
"created_at": 1790409978,
"updated_at": 1790409998
}
}
필드 설명(create / list / detail 세 인터페이스가 같은 필드 집합을 공유합니다):
| 필드 | 타입 | 설명 |
|---|---|---|
voice_id |
string | 음색 ID. 이후 커버에 사용합니다 |
clip_id / song_id |
string | 이 음색 오디오의 Suno 측 clip id(둘은 동일). 조사용 |
account_id |
string | 담당 계정 식별자(조사용, 클라이언트는 신경 쓰지 않아도 됩니다) |
title |
string | 음색 이름 |
source_file_name |
string | 원본 파일명 |
audio_url |
string | 원본 오디오의 장기 직링크. 미리듣기에 쓸 수 있고 영구 보관됩니다 |
image_url |
string | 음색 커버(Suno가 보컬 오디오에 대해 생성하는 커버). 커버가 없으면 이 키 자체가 나타나지 않습니다 |
duration_s |
number | 음색 샘플 길이(초) |
enhanced |
bool | 강화 클론으로 만들었는지 |
source_kind |
string | upload(업로드) / record(녹음) / clip(기존 오디오에서) |
status |
string | ready / processing / failed |
error_message |
string | 실패 사유. failed일 때만 나타납니다 |
task_id |
number | 강화 클론의 하위 작업 번호. enhanced=true일 때만 값이 있습니다 |
pre_consumed_quota |
number | 선과금 쿼터(강화 클론만 값이 있음) |
created_at / updated_at |
number | Unix 초 |
created_at/updated_at은 초 단위 타임스탬프(10자리)이며 밀리초가 아닙니다.
3.4 자주 나오는 오류
| HTTP | message | 원인과 대응 |
|---|---|---|
| 200 | one of file_path, file_base64, or file_url is required |
오디오 내용을 첨부하지 않았습니다 |
| 200 | Uploaded audio is too short (currently 2.4 seconds). Minimum duration is 6 seconds. |
샘플이 너무 짧습니다. 6초 이상(10초 이상 권장)으로 |
| 200 | This audio matches an existing recording in our catalog. |
카탈로그의 기존 녹음과 매우 비슷합니다. 다른 깨끗한 건성 보컬로 |
| 200 | upload succeeded without clip or account identity |
플랫폼 응답 이상. 다시 시도하세요 |
| 200 | audio file exceeds 30 MB limit |
서버 상한 초과(고객용으로는 10MB 이내로 유지하세요) |
| 401 | invalid token |
API Key 오류 |
3.5 강화 클론(enhanced=true)
"음색 샘플 자체에 저작권 있는 가사가 있다 / 강화 업로드가 필요하다"는 상황용입니다. 가격은 강화 업로드 가격(기본 10위안/회, 실패 시 자동 환불):
- 응답은 즉시
status=processing+task_id(이 시점에clip_id는 없음)이며 비동기로 실행됩니다. - 처리 중에는
GET /api/music/voice-profile/{voice_id}로 진행 상황을 조회할 수 있고progress_percent/progress_stage/progress_message를 반환합니다. - 완료되면
status가ready가 되고clip_id,audio_url,image_url이 채워집니다. - 실패하면
status=failed가 되고error_message는 "강화 업로드에 실패했습니다. 비용은 자동 환불되었습니다. 잠시 후 다시 시도하세요." 같은 문구입니다. - 강화 클론은 일반 생성보다 확실히 오래 걸립니다(분 단위). 동기 타임아웃 로직으로 기다리지 마세요.
curl -sS -X POST "https://www.suno-api.io/api/music/voice-profile/create" \
-H "Authorization: Bearer $API_KEY" \
-F "file=@./my-voice.mp3" -F "title=내 음색" -F "enhanced=true"
4. 기존 오디오로 음색 만들기
다시 업로드하고 싶지 않다면: 내가 이미 업로드했거나 이미 생성한 오디오를 그대로 음색으로 복사합니다. 무료이고 멱등입니다.
POST /api/music/voice-profile/create-from-clip
Content-Type: application/json
{
"source_song_id": "97ee62fe-56cd-473f-a2f5-c66ac0766a2e",
"title": "쉬칭창",
"client_request_id": "vp-clip-1789…"
}
source_song_id: 내 명의의 완료된 오디오 clip id(곡의song_id와 동일).- 같은 clip을 다시 보내도 새 레코드는 만들어지지 않고 기존 음색을
reused: true와 함께 그대로 반환합니다(실측).
응답은 §3.3의 모든 필드에 읽기 전용 두 개를 더한 것입니다:
| 필드 | 설명 |
|---|---|
reused |
true = 이 오디오는 이전부터 내 음색이었고 이번에 그대로 재사용 |
source_type |
그 오디오의 출처: generate / custom_generate / voice_generate / upload_source / enhanced_upload |
{
"success": true,
"data": {
"voice_id": "vp_20260926081712557959800Z5gvlt",
"clip_id": "97ee62fe-56cd-473f-a2f5-c66ac0766a2e",
"title": "쉬칭창",
"status": "ready",
"enhanced": true,
"reused": true,
"source_type": "enhanced_upload"
}
}
오류(message 원문 그대로. 그대로 표시하거나 현지화를 권장):
| message | 의미 |
|---|---|
source audio not found |
clip이 없거나 내 것이 아닙니다 |
source audio is invalid |
그 오디오가 아직 생성 중이거나 중간 산출물이라 아직 쓸 수 없습니다 |
source_song_id is required |
source_song_id를 보내지 않았습니다 |
5. 음색 목록 / 상세 / 삭제
5.1 목록
GET /api/music/voice-profile/list
GET /api/music/voice-profile/list?include_deleted=true
{
"success": true,
"message": "",
"data": {
"items": [ { "voice_id": "vp_…", "title": "내 음색", "status": "ready", "audio_url": "…", "image_url": "…" } ],
"total": 1
}
}
필드는 §3.3과 완전히 같습니다. 기본적으로 삭제된 음색은 반환하지 않습니다.
5.2 상세
GET /api/music/voice-profile/{voice_id}
음색 객체 하나를 반환합니다(data는 배열이 아니라 객체 자체). 강화 클론이 처리 중이면 다음을 추가로 반환합니다:
| 필드 | 설명 |
|---|---|
progress_percent |
0~100 정수 |
progress_stage |
단계 식별자. 예: media_preparing |
progress_message |
사용자에게 보여줄 진행 문구 |
5.3 삭제
POST /api/music/voice-profile/delete
Content-Type: application/json
{ "voice_id": "vp_2026…" }
{ "success": true, "message": "", "data": { "voice_id": "vp_2026…", "status": "deleted" } }
- 삭제하면 목록에서 사라지고 새 커버에 쓸 수 없습니다: 그
voice_id로 제출하면 400을 반환합니다(§6.4 참고). - 그 음색으로 만든 곡은 전혀 영향받지 않습니다. 재생, 다운로드, 이어쓰기, 구간 교체, 분리는 그대로 동작합니다.
- 없는 음색이나 이 계정 소유가 아닌 음색을 삭제하면
{"success":false,"message":"voice not found"}를 반환합니다.
6. 음색으로 커버하기(핵심 인터페이스)
POST /api/music/voice-profile/cover
Content-Type: application/json
한 번 제출하면 2곡을 생성합니다(일반 생성과 동일).
6.1 두 시나리오
| 시나리오 | 발동 조건 | 설명 |
|---|---|---|
| A. 지정한 곡 커버 | song_clip_id를 보냄 |
내 음색으로 그 곡을 부릅니다(멜로디 / 리듬 / 구성은 원곡 그대로, 보컬만 내 음색으로) |
| B. 음색으로 신곡 부르기 | song_clip_id를 보내지 않음 |
음색 + 가사(선택) + 스타일 요구사항만 주면 모델이 그 스타일로 새 멜로디를 만들어 부릅니다 |
응답의 resolved_scene이 cover 또는 sing을 그대로 돌려주므로 어느 경로를 탔는지 확인할 수 있습니다.
6.2 요청 필드
| 필드 | 타입 | 필수 | 기본 | 설명 |
|---|---|---|---|---|
model |
string | 예 | — | 고객용 모델명, §6.5 참고. 보내지 않으면 model is required(HTTP 500)가 납니다 |
voice_id |
string | 둘 중 하나 | — | 음색 ID(vp_…). status=ready여야 합니다 |
voice_clip_id |
string | 둘 중 하나 | — | 음색의 clip id를 바로 써도 됩니다. 둘 다 보내면 voice_id 우선 |
song_clip_id |
string | 아니오 | 비어 있음 | 커버할 곡의 clip id(song_id). 보내지 않으면 시나리오 B |
lyrics |
string | 아니오 | 비어 있음 | 가사 전문. 한 글자도 고치거나 자르지 않고 그대로 전송. 3000자 이하 |
cover_requirements |
string | 아니오 | 비어 있음 | 커버 요구사항(자연어). 예: "발음을 더 또렷하게". 고정 프롬프트에 이어 붙습니다 |
style_requirements |
string | 아니오 | 비어 있음 | 스타일 요구사항. 예: "슬로우 포크, 어쿠스틱 기타" |
title |
string | 아니오 | 비어 있음 | 이 두 곡의 워크스페이스 제목(이후 조회에서 반환되는 song_title에 영향을 줍니다) |
negative_tags |
string | 아니오 | 비어 있음 | 제외 스타일. 예: electronic, rap. 1000자 이하 |
vocal_gender |
string | 아니오 | 비어 있음 | m / f(male / female도 허용). 비우면 지정 없음 |
duration |
number | 아니오 | 비어 있음 | 희망 길이(초), 1~480 정수. 비우면 플랫폼이 결정. 플랫폼의 실제 최소는 약 30초: 더 작은 값을 넣어도 실측에서는 약 30초짜리 완성품만 나옵니다 |
audio_weight |
number | 아니오 | 0.85 | 오디오 참조도. 0~1 소수. 높을수록 음색 샘플에 가까워집니다 |
style_weight |
number | 아니오 | 비어 있음 | 스타일 영향. 0~1 소수 |
weirdness_constraint |
number | 아니오 | 비어 있음 | 창의성. 0~1 소수(화면이 퍼센트라면 100으로 나누세요) |
variety |
number | 아니오 | 비어 있음 | 다양성. 0~4 정수(퍼센트 아님). v6 계열만 지원 |
max_mode |
boolean | 아니오 | false |
Max Mode. 가격 ×2 |
client_request_id |
string | 아니오 | 비어 있음 | 제출 식별자. 서버 작업 기록에 남아 조사에 쓰입니다(중복 제거는 하지 않습니다) |
요청 헤더 X-Create-Ui-Client-Request-Id: <같은 제출 id>도 함께 넣기를 권장합니다: 이것만이 서버 측 작업 기록에 남아 사후 조사의 기본 키가 됩니다(§1.5 참고).
단위 주의: audio_weight / style_weight / weirdness_constraint는 0~1 소수, variety는 0~4 정수, duration은 초입니다. 혼동하면 거부되거나 결과가 이상해집니다.
시나리오 B의 가사는 선택입니다(모델이 알아서 처리합니다). 다만 지정한 내용을 부르게 하려면 반드시 lyrics를 보내세요.
6.3 응답
{
"code": 200,
"message": "success",
"data": {
"voice_id": "vp_20260926081712557959800Z5gvlt",
"voice_clip_id": "97ee62fe-56cd-473f-a2f5-c66ac0766a2e",
"song_clip_id": "5b773e9a-a035-4134-a4a4-29c9961d3ccb",
"resolved_scene": "cover",
"model": "suno-v6",
"audio_weight": 0.85,
"clips": [
{
"song_id": "0f2a…",
"status": "processing",
"song_title": "",
"model_version": "suno-v6",
"audio_url": "",
"play_url": "https://media.suno-api.io/play/0f2a…?expires=…&signature=…",
"lyrics": "",
"description": "[작업] 이것은 커버입니다.…(서버의 고정 구조부 + 내가 입력한 커버 / 스타일 요구사항)"
},
{
"song_id": "7c91…",
"status": "processing",
"model_version": "suno-v6",
"audio_url": ""
}
],
"source_updates": []
}
}
핵심:
- 완성품 식별은 **
clips[].song_id**입니다(이 인터페이스는id필드를 반환하지 않습니다). - 즉시 2개의
song_id를 받습니다. 이때status는 아직processing(접수만 의미)이고audio_url은 비어 있습니다. 다운로드하려면completed까지 폴링해야 합니다.play_url은 플랫폼 미리듣기 스트림이며 제출 단계에서 이미 나타날 수 있고 만료됩니다. description은 서버가 최종적으로 전송하는 프롬프트(고정 구조부 + 내가 입력한cover_requirements/style_requirements)이며, 결과를 조사할 때 유용합니다.source_updates는 서버의 소재 처리 결과이며 클라이언트는 무시해도 됩니다.- 내가 보낸
title은 서버 작업 기록에 남아 이후 조회에서song_title로 나타납니다. 제출 응답의clips[].song_title은 보통 비어 있고, 이는 정상입니다.
6.4 오류
{ "error": { "message": "이 음색은 존재하지 않거나 삭제되었습니다(voice_id=vp_xxx)…", "type": "new_api_error", "param": "", "code": "invalid_request" } }
| HTTP | message | 원인 |
|---|---|---|
| 500 | model is required |
본문에 model이 없습니다(채널 선택의 필수 필드입니다) |
| 400 | voice_id is required and must be a ready voice |
voice_id를 보내지 않았거나, 자리표시자 값 / 준비되지 않은 음색을 보냈습니다 |
| 400 | 이 음색은 존재하지 않거나 삭제되었습니다(voice_id=vp_xxx)… |
음색이 삭제되었거나, 없거나, 현재 계정 소유가 아닙니다. 다른 사용 가능한 음색으로 바꾸거나 새로 만드세요 |
| 400 | 이 음색은 삭제되었습니다(voice_id=vp_xxx)… |
voice_clip_id로 소재를 직접 보낼 때, 그 소재에 해당하는 음색이 삭제되었습니다. 대응은 위와 같습니다 |
| 400 | voice is not ready yet (status=processing) |
음색이 아직 생성 중입니다. ready가 된 뒤 다시 시도하세요 |
| 400 | song_clip_id must be a real completed clip |
song_clip_id가 자리표시자 / 미완료입니다 |
| 400 | song_clip_id must differ from the voice clip |
커버 대상이 음색 샘플 자체일 수는 없습니다 |
| 400 | audio_weight must be a number between 0 and 1 |
슬라이더 단위 실수(퍼센트를 보냈습니다) |
| 400 | duration must be an integer between 1 and 480 seconds |
길이가 1~480 정수가 아닙니다 |
| 400 | vocal_gender must be one of m, f, male, female |
성별 값이 잘못되었습니다 |
| 400 | variety must be an integer between 0 and 4 / variety is only supported on v6 models |
다양성 값 또는 모델이 지원되지 않습니다 |
잔액 부족은 제출 단계에서 바로 과금 오류를 반환하며, 어중간한 작업을 남기지 않습니다.
6.5 사용 가능한 모델(고객용 모델명)
| 고객용 모델명 | 설명 |
|---|---|
suno-v6 |
기본, 권장 |
suno-v6-wild |
더 자유분방 |
suno-v6-mini |
경량 |
고객용 모델명(위의 이름)을 쓰세요. chirp-*는 플랫폼 내부 이름이므로 직접 쓰지 마세요.
7. 진행 상황 조회
7.1 일괄 조회(권장)
POST /api/music/query
Content-Type: application/json
{ "model": "suno-v6", "song_ids": "id1,id2" }
song_ids는 반각 쉼표로 구분한 문자열입니다(배열이 아닙니다).- 반환은 맨 배열(바로
[...],success/data껍데기 없음)입니다:
[
{
"song_id": "0f2a…",
"song_title": "두 마리 호랑이",
"status": "completed",
"audio_url": "https://suno-api-hk-1437223057.cos.ap-hongkong.myqcloud.com/assets/0f2a…-mp3.mp3",
"cover_url": "https://cdn2.suno.ai/image_0f2a….jpeg",
"duration": 115.15,
"model_version": "suno-v6",
"generation_type": "voice_profile_cover",
"play_url": "https://media.suno-api.io/play/…?expires=…&signature=…",
"lyrics": "[Verse 1]…",
"prompt": "[Verse 1]…",
"description": "[작업] 이것은 커버입니다.…(서버의 최종 프롬프트)",
"style_tags": "…",
"created_at": "2026-09-25T18:19:09.918Z",
"video_url": ""
}
]
| 필드 | 설명 |
|---|---|
status |
§1.7 참고. completed만 완료로 봅니다 |
audio_url |
mp3 직링크. completed 이후에만 값이 있습니다 |
cover_url |
커버 이미지 주소 |
duration |
실제 길이(초) |
generation_type |
음색 커버가 만든 곡은 항상 voice_profile_cover |
play_url |
서명된 미리듣기 주소(만료됨) |
song_title |
작업 제목(제출 시 보낸 title. 보내지 않으면 서버 기본값) |
lyrics / prompt |
완성품 가사 / 가사 원문 |
description |
서버가 최종 전송하는 프롬프트(고정 구조부 포함) |
style_tags |
최종 스타일 설명 |
created_at |
ISO 8601 문자열(상세 인터페이스의 Unix 초와 다르니 주의) |
폴링 권장: 510초 간격, 전체 타임아웃 1015분(콜드 계정의 가장 느린 실측은 약 4분 30초. 아주 드물게 더 오래 걸립니다). 완료 판정은 audio_url이 비었는지가 아니라 status로 하세요.
호환 메모: 이 인터페이스는
song_id를 식별자로 씁니다. 초기 일부 응답에id필드가 나타난 적이 있으므로row.song_id || row.id로 쓰는 것이 안전합니다.
7.2 곡 단건 상세
GET /api/music/songs/{song_id}
{
"success": true,
"data": {
"song_id": "0f2a…",
"title": "두 마리 호랑이",
"status": "completed",
"generation_type": "voice_profile_cover",
"model": "suno-v6",
"duration": 115.15,
"audio_url": "https://…/assets/0f2a…-mp3.mp3",
"image_url": "https://cdn2.suno.ai/image_0f2a….jpeg",
"wav_url": "https://www.suno-api.io/api/music/download?song_id=0f2a…&kind=wav",
"video_url": "https://www.suno-api.io/api/music/download?song_id=0f2a…&kind=video",
"created_at": 1790411012,
"completed_at": 1790411500,
"parameters": {
"prompt": "[Verse 1]…",
"style_tags": "…",
"title": "두 마리 호랑이",
"model": "suno-v6",
"generation_type": "voice_profile_cover",
"source_song_id": "5b773e9a-…"
},
"parameter_retention": {
"core_parameters_available": true,
"extended_parameters_available": false,
"sources": ["suno_tasks"],
"unavailable_fields": ["negative_tags", "vocal_gender", "style_weight", "weirdness_constraint"]
}
}
}
- 여기서
created_at/completed_at은 Unix 초입니다(일괄 조회의 ISO 문자열과 다르니 클라이언트가 구분해야 합니다). wav_url/video_url은 이 플랫폼의 다운로드 진입점(현재 도메인 기준)이며GET으로 파일을 받습니다. 플랫폼에 해당 산출물이 없으면 빈 문자열입니다.parameter_retention은 그 곡에서 어떤 제출 파라미터를 추적할 수 있는지 알려줍니다.core_parameters_available=true는 가사 / 스타일 / 출처 등 핵심 파라미터를 조회할 수 있다는 뜻이고,extended_parameters_available=false일 때는 위에 나열한 확장 파라미터가 남아 있지 않다는 뜻입니다(음색 커버가 현재 이 분류입니다).
7.3 내 작품 목록
GET /api/music/songs?p=1&page_size=20
GET /api/music/songs?status=completed&q=키워드
페이징 파라미터: p(페이지 번호, 1부터) / page_size(페이지당 건수, 호환 표기 ps). 선택 사항 status, q(제목 키워드). 기본은 생성 시각 내림차순입니다.
{
"success": true,
"data": {
"page": 1,
"page_size": 20,
"total": 42,
"items": [
{
"song_id": "0f2a…",
"title": "두 마리 호랑이",
"status": "completed",
"generation_type": "voice_profile_cover",
"model": "suno-v6",
"duration": 115.15,
"audio_url": "https://…/assets/0f2a…-mp3.mp3",
"image_url": "https://cdn2.suno.ai/image_0f2a….jpeg",
"created_at": 1790411012,
"completed_at": 1790411500
}
]
}
}
generation_type=voice_profile_cover로 "음색 커버가 만든 곡"과 다른 생성 결과를 구분할 수 있습니다.
8. 완성품 다운로드
8.1 mp3 바로 받기(권장, 무료)
셋 다 되므로 하나만 고르세요:
- 조회 결과의
audio_url사용(오브젝트 스토리지 직링크, 가장 간편). GET /api/music/download?song_id=<id>&kind=mp3→ 오디오 바이트 스트림을 바로 반환합니다(Content-Disposition에 파일명 포함).POST /api/music/download-url(서명된 임시 주소 획득).
curl -sS -L "https://www.suno-api.io/api/music/download?song_id=0f2a…&kind=mp3" \
-H "Authorization: Bearer $API_KEY" -o song.mp3
kind 값: mp3(song과 동일) / playback(스트리밍 재생) / wav / video / lyrics / timestamped_lyrics.
timestamped_lyrics(.lrc)는 현재 일부 곡만 지원합니다. 실패하면 시간축 가사 API로 직접 생성하세요.
응답은 그대로 파일 바이트 스트림이며 Content-Type과 Content-Disposition(파일명)이 이미 설정되어 있습니다. GET /api/music/songs/{song_id}가 반환하는 wav_url / video_url은 kind=wav|video를 붙인 같은 진입점입니다.
8.2 wav 변환 / 가사 / 메타데이터(과금)
POST /api/music/free2
Content-Type: application/json
{ "song_ids": ["0f2a…"], "files": ["mp3", "wav", "lyrics"] }
{
"success": true,
"data": {
"songs": [
{ "song_id": "0f2a…", "files": [ { "type": "mp3", "url": "https://…" }, { "type": "wav", "url": "https://…" } ] }
],
"requested_songs": 1,
"succeeded_songs": 1,
"price": 0.2
}
}
files지원 값:mp3/wav/lyrics/metadata, 한 번에 최대 10곡.- 성공한 곡 수로 과금(곡당 0.2위안). 곡 전체가 실패하면 과금되지 않고, 특정 형식만 실패하면
songs[].errors에 나타납니다. - 이전에 받았던 파일을 다시 받으려면 먼저 무료인
POST /api/music/free2/cached(같은song_ids+files)로 아카이브 주소를 받으세요. 그 반환의songs[].missing_files가 캐시되지 않아 다시 준비해야 하는 형식을 알려줍니다.
9. 가격 조회
GET /api/pricing/catalog
인증이 필요 없는 공개 인터페이스입니다. 고객에게 표시할 단가는 모두 data.catalog.meta(문자열)에 있습니다:
| 키 | 의미 | 현재 값 |
|---|---|---|
generation_price |
일반 생성 | 0.6 |
voice_generation_price |
음색 커버 기준가 | 1.2 |
max_mode_factor |
Max Mode 배율 | 2 |
enhanced_upload_price |
강화 클론 / 강화 업로드 | 10 |
voice_auto_verify_price |
음성 자동 검증 | 5 |
voice_long_term_clone_price |
장기 음색 클론 | 5 |
가격은 백오피스에서 조정되므로 실시간으로 읽고 클라이언트에 하드코딩하지 마세요. 커버 실제 과금 = voice_generation_price ×(max_mode가 참이면 max_mode_factor를 한 번 더).
10. 제한과 주의사항
- 업로드 크기: 10MB 이하(서버 허용 상한은 더 높지만, 진입 게이트웨이가 10MiB에서 큰 본문을 잘라 "Suno가 거부함"처럼 보입니다. 미리 막으세요).
- 샘플 길이: 최소 6초, 10~60초 권장. 너무 짧으면 플랫폼이 거부합니다.
- 샘플 내용: 깨끗한 보컬. 기존 녹음과 매우 비슷한 것은 피하세요(카탈로그 대조에서 거부됩니다).
- 텍스트 상한:
lyrics는 3000자 이하.style_requirements/negative_tags/title은 각각 1000 / 1000 / 100자 이내를 권장(스타일과 커버 요구사항은 프롬프트에 이어 붙으므로 합계를 3000자 이내로 유지하세요). - 가사는 고치지 않습니다: 준 그대로 부릅니다(한 글자도 다르지 않게). 로컬에서 자르지도 않습니다.
- 한 곡당 반드시 2곡을 산출합니다. 1곡만 돌아온다고 가정하지 마세요.
- 원본 오디오는 영구 보관: 음색을 삭제해도 원본 오디오는 정리되지 않습니다. 이는 이후 커버를 가능하게 하고 규정 추적을 유지하기 위해서입니다.
- 음색은 ready여야 합니다:
processing상태의 음색은 커버에 쓸 수 없습니다. - 요청 타임아웃: 콜드 계정의 첫 제출은 실측 최대 약 4분 30초입니다. 클라이언트 타임아웃은 6분 이상으로 설정하세요. 커버는 요청 단위 중복 제거를 하지 않으므로, 타임아웃 후 새 곡이 이미 생겼는지 확인(§1.5)한 뒤 재전송을 결정하세요.
- 음색 소유권: 음색은 만든 사람에게 귀속되어 관리됩니다. 커버는 준비 완료(
ready)이고 삭제되지 않은 음색만 받습니다. 없거나, 삭제되었거나, 이 계정 소유가 아니면 400을 반환합니다:이 음색은 존재하지 않거나 삭제되었습니다(voice_id=…).
11. 자주 묻는 질문(FAQ)
Q1. 음색 만들기에 돈이 드나요? 일반 생성과 "기존 오디오에서 만들기"는 모두 무료입니다. 과금되는 것은 커버(1.2위안 / MAX 2.4위안)와 강화 클론(기본 10위안)뿐입니다.
Q2. 반주가 있는 완성곡을 음색 샘플로 써도 되나요?
권장하지 않습니다. 음악이 들어 있거나 카탈로그의 기존 녹음과 매우 비슷하면 플랫폼이 거부합니다(This audio matches an existing recording in our catalog.). 순수 건성 보컬을 쓰세요.
Q3. 커버 제출 후 audio_url이 비어 있는 이유는?
제출은 접수만 된 것이고 status는 아직 processing입니다. /api/music/query를 status=completed까지 폴링해야 재생 / 다운로드 가능한 주소가 나옵니다.
Q4. 시나리오 B(song_clip_id를 보내지 않음)에서 가사를 보내야 하나요?
선택입니다. 지정한 내용을 부르게 하려면 lyrics를 보내세요. 보내지 않으면 모델이 알아서 처리하지만 제어성이 떨어집니다.
Q5. 고객이 직접 업로드한 오디오를 바로 음색으로 쓸 수 있나요?
됩니다. create의 file_url 형식을 쓰거나, 먼저 곡으로 업로드한 뒤 create-from-clip을 호출하세요.
Q6. 실패하면 환불되나요?
바로 거부된 요청(파라미터 오류 / 플랫폼 오류)은 실측에서 과금되지 않습니다. 접수되었지만 최종 실패한 작업은 선과금액이 환불됩니다. 종료 실패 시 작업은 status=failed와 error_message를 반환합니다.
Q7. 음색을 삭제한 뒤 그 음색으로 만든 곡을 계속 들을 수 있나요? 들을 수 있습니다. 삭제는 그 음색을 새 커버에 쓸 수 있는지에만 영향을 줍니다.
Q8. 한 번에 얼마나 긴 곡을 커버할 수 있나요?
duration은 1~480초입니다. 보내지 않으면 플랫폼이 원곡 / 소재를 기준으로 자동 결정합니다. 플랫폼의 완성품은 최소 약 30초라는 점에 주의하세요. 30초 미만을 지정해도 실측에서는 29.96 / 29.76초였습니다.
부록 A: 전체 호출 예시(Node.js, 폴링과 다운로드 포함)
const BASE = "https://www.suno-api.io";
const KEY = process.env.SUNO_API_KEY;
const H = { Authorization: `Bearer ${KEY}` };
// ① 음색 만들기(multipart)
async function createVoice(filePath, title) {
const fd = new FormData();
fd.set("file", new Blob([await (await import("node:fs/promises")).readFile(filePath)], { type: "audio/mpeg" }), "my-voice.mp3");
fd.set("file_name", "my-voice.mp3");
fd.set("source_file_name", "my-voice.mp3");
fd.set("title", title);
fd.set("client_request_id", `vp-create-${Date.now()}`);
const res = await fetch(`${BASE}/api/music/voice-profile/create`, { method: "POST", headers: H, body: fd });
const body = await res.json();
if (!body.success) throw new Error(body.message);
return body.data; // { voice_id, status, audio_url, ... }
}
// ② 커버
async function cover(voiceId, songClipId, lyrics) {
const submissionId = `vp-cover-${Date.now()}`;
const res = await fetch(`${BASE}/api/music/voice-profile/cover`, {
method: "POST",
headers: { ...H, "Content-Type": "application/json", "X-Create-Ui-Client-Request-Id": submissionId },
body: JSON.stringify({
model: "suno-v6",
voice_id: voiceId,
song_clip_id: songClipId || "",
lyrics: lyrics || "",
style_requirements: "슬로우 포크, 어쿠스틱 기타",
audio_weight: 0.85,
max_mode: false,
client_request_id: submissionId,
}),
});
const body = await res.json();
if (body.error) throw new Error(body.error.message);
return body.data; // { resolved_scene, clips: [{ song_id, status }] }
}
// ③ 완료까지 폴링(5초 간격, 최대 15분)
async function waitUntilCompleted(songIds) {
const deadline = Date.now() + 15 * 60 * 1000;
while (Date.now() < deadline) {
const res = await fetch(`${BASE}/api/music/query`, {
method: "POST",
headers: { ...H, "Content-Type": "application/json" },
body: JSON.stringify({ model: "suno-v6", song_ids: songIds.join(",") }),
});
const songs = await res.json(); // 맨 배열
if (songs.every((s) => s.status === "completed" || s.status === "failed")) return songs;
await new Promise((r) => setTimeout(r, 5000));
}
throw new Error("timeout waiting for cover");
}
// ④ 다운로드
async function download(songId, outPath) {
const res = await fetch(`${BASE}/api/music/download?song_id=${encodeURIComponent(songId)}&kind=mp3`, { headers: H });
if (!res.ok) throw new Error(`download failed: ${res.status}`);
(await import("node:fs/promises")).writeFile(outPath, Buffer.from(await res.arrayBuffer()));
}
const voice = await createVoice("./my-voice.mp3", "내 음색");
const result = await cover(voice.voice_id, "5b773e9a-a035-4134-a4a4-29c9961d3ccb", "[Verse 1]\n가사…");
const ids = result.clips.map((c) => c.song_id);
const songs = await waitUntilCompleted(ids);
for (const [i, s] of songs.entries()) {
if (s.status === "completed") await download(s.song_id, `./out-${i}.mp3`);
}
부록 B: 변경 기록
| 날짜 | 변경 |
|---|---|
| 2026-10-15 | 삭제 의미를 명확히 기재(§5.3). "삭제됨 / 권한 없음" voice_id로 커버하면 선과금 전에 400을 반환하도록 바뀌었고 오류 표와 FAQ도 동기화(같은 날 출시한 음색 삭제 게이트에 맞춤) |
| 2026-10-13 | 도입부에 "이 경로를 권장하는 이유" 추가: 다수 사용자의 실제 평가에서 이 경로가 생성한 곡은 원음 재현도가 높음 |
| 2026-09-26 | 초판: 음색 만들기(세 가지 입력 형식) / 기존 오디오에서 만들기 / 목록·상세·삭제 / 음색 커버(A, B 시나리오) / 진행 조회 / 다운로드 / 가격 읽기. 실측 근거 포함 |