음악 생성
Mureka 또는 Suno로 노래, 인스트루멘털, 사운드트랙을 생성하고 기존 오디오를 분석합니다 — 가사 인식, 오디오 설명, 스템 분리.
Music API는 텍스트, 가사, 이미지, 참조 오디오를 음악으로 바꾸고, 기존 트랙을 분석합니다. 생성 기능은 Mureka와 Suno 두 프로바이더가 담당하며, 둘 다 지원하는 엔드포인트에서는 요청마다 provider 필드로 선택합니다. 모든 엔드포인트는 https://api.marswave.ai/openapi/v1/music 아래에 있으며 Authorization: Bearer $LISTENHUB_API_KEY로 인증합니다.
이 API는 두 가지 응답 패턴으로 나뉩니다.
| 패턴 | 엔드포인트 | 결과를 받는 방식 |
|---|---|---|
| 비동기 생성 | /generate, /cover, /instrumental, /soundtrack, /track, /remix, /extend | 202와 taskId를 반환합니다. status가 success가 될 때까지 GET /v1/music/tasks/{taskId}를 폴링(polling)합니다. |
| 동기 분석 | /recognize, /describe, /stem | 200을 반환하고 결과가 같은 응답에 담깁니다. 폴링이 필요 없습니다. |
모든 응답은 { "code": 0, "message": "", "data": { ... } } 형태로 감싸집니다. code가 0이 아니면 오류입니다 — 오류 처리를 참고하세요. 아래 예제는 모두 data에서 필드를 읽습니다.
프로바이더
provider는 mureka, suno, default를 받으며 default는 Mureka로 해석됩니다. 이 필드를 읽는 엔드포인트는 둘뿐이고, 나머지는 한 프로바이더가 전담하므로 값을 넘겨도 무시합니다.
| 엔드포인트 | Mureka | Suno | 프로바이더 결정 방식 |
|---|---|---|---|
/generate | ✓ | ✓ | provider로 지정, 기본값 Mureka |
/extend | ✓ | ✓ | provider로 지정, 기본값 Mureka |
/cover | — | ✓ | 항상 Suno |
/instrumental | ✓ | — | 항상 Mureka |
/soundtrack | ✓ | — | 항상 Mureka |
/track | ✓ | — | 항상 Mureka |
/remix | ✓ | — | 항상 Mureka |
/recognize | ✓ | — | 항상 Mureka |
/describe | ✓ | — | 항상 Mureka |
/stem | ✓ | — | 항상 Mureka |
/remix와 /cover는 둘 다 기존 녹음을 다시 연주하지만 서로 대체할 수 없습니다. /remix는 Mureka 곡을 새 가사로 다시 부르고, /cover는 업로드한 오디오를 Suno로 새 스타일에 맞춰 다시 녹음합니다.
GET /v1/music/tasks와 GET /v1/music/tasks/{taskId}는 두 프로바이더의 작업을 모두 반환합니다. 실제로 처리한 프로바이더는 각 작업의 provider 필드에 담깁니다.
모델
model의 값 범위는 요청을 처리하는 프로바이더에 따라 정해집니다. Mureka 엔드포인트에는 Mureka 모델 이름을, Suno 엔드포인트에는 Suno 등급을 넘깁니다. 크레딧 열은 등급별로 과금하는 /generate, /instrumental, /cover에 적용됩니다. /extend는 별도 표로 과금하며(mureka-7.6과 auto(과금 전에 mureka-7.6으로 해석됩니다)는 10크레딧, Suno 전 등급을 포함한 나머지 모델은 15크레딧), 그 밖의 엔드포인트는 고정 요금으로 크레딧에 정리되어 있습니다.
Mureka 모델:
| 모델 | 크레딧 | 설명 |
|---|---|---|
auto | 10 | Mureka 기본값. 서비스가 모델을 자동으로 선택합니다. |
mureka-7.6 | 5 | |
mureka-8 | 10 | |
mureka-9 | 10 | /instrumental에서는 사용할 수 없습니다. |
mureka-o2 | 10 |
Suno 모델:
| 모델 | 크레딧 | 설명 |
|---|---|---|
V4 | 15 | |
V4_5 | 15 | |
V4_5PLUS | 15 | |
V4_5ALL | 15 | |
V5 | 15 | |
V5_5 | 20 | |
V6 | 20 | Suno 기본값. 가장 최신 등급입니다. |
모든 등급은 계속 명시적으로 지정할 수 있습니다. model을 생략한 Suno 요청은 V6로 실행됩니다.
스템 분리(/stem)는 다른 모델 세트를 사용합니다: audio-separation-1(기본값) 또는 audio-separation-2(MIDI도 함께 생성).
Suno 전용 파라미터
Suno에는 Mureka에 대응 항목이 없는 파라미터가 일곱 개 있습니다. /generate와 /cover에서는 providerParams 안에 넣고, /extend에서는 최상위 폼 필드로 넘깁니다.
| 필드 | 타입 | 적용 엔드포인트 | 설명 |
|---|---|---|---|
negativeTags | string | /generate, /cover, /extend | 피하고 싶은 스타일 태그, 쉼표로 구분 |
vocalGender | string | /generate, /cover, /extend | m 또는 f. male / female을 받는 /track과 다르니 주의하세요 |
styleWeight | number | /generate, /cover, /extend | 0–1. 결과가 style을 얼마나 충실히 따를지 |
weirdnessConstraint | number | /generate, /cover, /extend | 0–1. 결과가 통상적인 형태에서 얼마나 벗어날 수 있는지 |
audioWeight | number | /generate, /cover, /extend | 0–1. 원본 녹음의 가중치. 원본 오디오를 함께 보내는 요청에서만 의미가 있습니다 |
personaId | string | /generate, /cover | 트랙을 부를 Suno persona. /cover에서는 커스텀 모드에서만 적용됩니다 |
personaModel | string | /generate, /cover | 해당 persona를 만든 모델 등급. personaId와 함께 보내세요 |
위 목록에 없는 키는 요청이 Suno에 도달하기 전에 providerParams에서 제거됩니다.
비동기 작업 라이프사이클
- 생성 요청을 보냅니다. 응답에는
taskId와 초기status인pending이 담깁니다. GET /v1/music/tasks/{taskId}를 폴링합니다.status는pending→generating→uploading→success순으로 진행됩니다.success가 되면 완성된tracks배열(제목, 태그, 길이, 서명된audioUrl)을 읽습니다.failed인 경우에는errorMessage를 읽습니다.params.model은 작업이 실제로 실행된 등급을 알려 주며,creditCost는 이 등급을 기준으로 매겨집니다.
권장 폴링 방식: 요청을 보낸 뒤 약 30초를 기다렸다가 10초마다 폴링합니다. 음악 작업은 보통 1–3분 안에 완료됩니다.
{
"code": 0,
"message": "",
"data": {
"id": "68e780390fc5c9a54f695a7e",
"provider": "mureka",
"taskType": "GENERATE",
"status": "success",
"params": {
"model": "auto",
"prompt": "r&b, slow, passionate, male vocal",
"instrumental": false
},
"tracks": [
{
"title": "Night Walk",
"tags": "r&b, slow",
"duration": 142.5,
"audioUrl": "https://assets.listenhub.app/.../track-1.mp3"
}
],
"creditCost": 10,
"createdAt": 1730000000000,
"updatedAt": 1730000180000
}
}트랙의 audioUrl 값은 서명된 URL이며 작업을 조회한 시점으로부터 약 1시간 뒤에 만료됩니다. 만료 전에 오디오를 내려받거나 작업을 다시 조회해 URL을 갱신하세요.
텍스트 또는 가사로 생성
POST /v1/music/generate
스타일 프롬프트와 가사(둘 중 하나 또는 둘 다)로 노래를 만듭니다. JSON을 전송합니다. provider=suno를 넘기지 않으면 Mureka가 처리합니다. Mureka는 인스트루멘털이 아니면서 lyrics가 없는 요청을 거부하고, Suno는 lyrics나 prompt 중 하나만 있어도 받습니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/generate" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"prompt": "r&b, slow, passionate, male vocal",
"lyrics": "[verse]\nWalking down the empty street at night\n[chorus]\nFeel the rhythm, feel the light",
"title": "Night Walk",
"model": "auto"
}'const response = await fetch('https://api.marswave.ai/openapi/v1/music/generate', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
prompt: 'r&b, slow, passionate, male vocal',
lyrics: '[verse]\nWalking down the empty street at night\n[chorus]\nFeel the rhythm, feel the light',
title: 'Night Walk',
model: 'auto',
}),
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/generate',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'prompt': 'r&b, slow, passionate, male vocal',
'lyrics': '[verse]\nWalking down the empty street at night\n[chorus]\nFeel the rhythm, feel the light',
'title': 'Night Walk',
'model': 'auto',
},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
prompt | string | 아니요 | Mureka에서는 스타일·설명 프롬프트. Suno는 이 값을 가사로 읽으므로 lyrics를 쓰고 스타일은 style로 넘기세요 |
lyrics | string | 아니요 | 가사. instrumental이 true가 아니면 Mureka에서는 필수. Suno는 이 값을 곡의 가사로 사용합니다 |
style | string | 아니요 | 스타일 태그. prompt의 대체값으로 사용됨 |
title | string | 아니요 | 트랙 제목 |
instrumental | boolean | 아니요 | 보컬 없이 생성 |
customMode | boolean | 아니요 | Suno 전용. 기본값은 true이며 이때 style과 title이 설명을 담고 가사 본문은 lyrics에서 가져옵니다. false로 두면 곡 전체를 prompt 하나로 생성합니다 |
model | string | 아니요 | 모델 참고. 기본값은 Mureka auto, Suno V6 |
vocalId | string | 아니요 | 기존 Mureka vocal id 재사용. Mureka 전용 |
provider | string | 아니요 | default(Mureka), mureka, suno 중 하나. 기본값은 default |
providerParams | object | 아니요 | 프로바이더별 파라미터. Suno는 Suno 전용 파라미터 참고 |
202와 함께 { "taskId": "...", "status": "pending" }을 반환합니다. 결과는 작업을 폴링해서 가져옵니다.
인스트루멘털 생성
POST /v1/music/instrumental
텍스트 프롬프트 또는 참조 오디오 파일로 단독 인스트루멘털을 만듭니다. prompt와 referenceAudio 중 정확히 하나만 제공하세요. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/instrumental" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "prompt=lofi hip hop, mellow, rainy night" \
-F "model=auto"const form = new FormData();
form.append('prompt', 'lofi hip hop, mellow, rainy night');
form.append('model', 'auto');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/instrumental', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/instrumental',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
data={'prompt': 'lofi hip hop, mellow, rainy night', 'model': 'auto'},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
prompt | string | 둘 중 하나 | 스타일·장르 설명. referenceAudio와 함께 쓸 수 없음 |
referenceAudio | file | 둘 중 하나 | 참조 오디오(mp3/m4a, 최대 10MB). prompt와 함께 쓸 수 없음 |
model | string | 아니요 | auto, mureka-7.6, mureka-8, mureka-o2 중 하나. 기본값은 auto |
이미지 또는 영상으로 사운드트랙 생성
POST /v1/music/soundtrack
이미지나 영상에 어울리는 음악을 생성합니다. image와 video 중 정확히 하나만 제공하세요. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/soundtrack" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "image=@scene.jpg" \
-F "prompt=cinematic, hopeful, orchestral" \
-F "model=auto"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('image', new Blob([await readFile('scene.jpg')]), 'scene.jpg');
form.append('prompt', 'cinematic, hopeful, orchestral');
form.append('model', 'auto');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/soundtrack', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
with open('scene.jpg', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/soundtrack',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'image': f},
data={'prompt': 'cinematic, hopeful, orchestral', 'model': 'auto'},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
image | file | 둘 중 하나 | 이미지(jpg/jpeg/png/webp). video와 함께 쓸 수 없음 |
video | file | 둘 중 하나 | 영상(mp4/mov/avi/mkv/webm). image와 함께 쓸 수 없음 |
prompt | string | 아니요 | 스타일·설명 프롬프트 |
model | string | 아니요 | auto, mureka-7.6, mureka-8, mureka-9, mureka-o2 중 하나. 기본값은 auto |
단일 트랙 생성
POST /v1/music/track
참조 오디오 파일 또는 기존 Mureka providerSongId로 악기 트랙이나 보컬 트랙 하나를 생성합니다. audio와 providerSongId 중 정확히 하나만 제공하세요. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/track" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@reference.mp3" \
-F "generateType=Drums" \
-F "prompt=funk, tight groove, 110 bpm"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('reference.mp3')]), 'reference.mp3');
form.append('generateType', 'Drums');
form.append('prompt', 'funk, tight groove, 110 bpm');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/track', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
with open('reference.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/track',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
data={'generateType': 'Drums', 'prompt': 'funk, tight groove, 110 bpm'},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
generateType | string | 예 | 생성할 트랙 종류. Vocals, Instrumental, Drums, Bass, Guitar, Keyboard, Percussion, Strings, Synth, FX, Brass, Woodwinds 중 하나 |
prompt | string | 예 | 스타일·장르 설명 |
audio | file | 둘 중 하나 | 참조 오디오(mp3/m4a/wav, 최대 10MB). providerSongId와 함께 쓸 수 없음 |
providerSongId | string | 둘 중 하나 | 이전 결과에서 얻은 Mureka song id. audio와 함께 쓸 수 없음 |
lyrics | string | Vocals일 때 | 가사. generateType이 Vocals이면 필수 |
vocalGender | string | 아니요 | male 또는 female. generateType=Vocals일 때만 사용 |
generateStart | number | 아니요 | 구간 시작 지점(초) |
generateEnd | number | 아니요 | 구간 종료 지점(초) |
기존 곡 리믹스
POST /v1/music/remix
기존 곡을 새 가사로 다시 부르게 합니다. 원본 오디오는 업로드한 audio 파일, ListenHub 내부 audioUrl, Mureka providerSongId 중 정확히 한 가지 방식으로 제공하세요. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/remix" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@original.mp3" \
-F "lyrics=[verse]\nA brand new story to tell" \
-F "prompt=upbeat pop, bright synths"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('original.mp3')]), 'original.mp3');
form.append('lyrics', '[verse]\nA brand new story to tell');
form.append('prompt', 'upbeat pop, bright synths');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/remix', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
with open('original.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/remix',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
data={
'lyrics': '[verse]\nA brand new story to tell',
'prompt': 'upbeat pop, bright synths',
},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
lyrics | string | 예 | 새 가사 |
prompt | string | 예 | 스타일·장르 설명 |
audio | file | 소스 중 하나 | 오디오 파일(mp3/m4a, 최대 10MB) |
audioUrl | string | 소스 중 하나 | ListenHub 내부 오디오 URL(본인 소유이거나 공개 상태여야 함) |
providerSongId | string | 소스 중 하나 | 이전 결과에서 얻은 Mureka song id |
기존 녹음 커버
POST /v1/music/cover
업로드한 트랙의 멜로디는 유지한 채 새로운 스타일로 다시 녹음합니다. Suno 전용입니다. JSON을 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/cover" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"uploadUrl": "https://example.com/original.mp3",
"style": "acoustic folk, fingerpicked guitar",
"title": "Night Walk (Acoustic)",
"prompt": "[verse]\nWalking down the empty street at night",
"model": "V6"
}'const response = await fetch('https://api.marswave.ai/openapi/v1/music/cover', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
uploadUrl: 'https://example.com/original.mp3',
style: 'acoustic folk, fingerpicked guitar',
title: 'Night Walk (Acoustic)',
prompt: '[verse]\nWalking down the empty street at night',
model: 'V6',
}),
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/cover',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
json={
'uploadUrl': 'https://example.com/original.mp3',
'style': 'acoustic folk, fingerpicked guitar',
'title': 'Night Walk (Acoustic)',
'prompt': '[verse]\nWalking down the empty street at night',
'model': 'V6',
},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
uploadUrl | string | 예 | 원본 녹음. 공개적으로 접근 가능한 URL이거나 본인 소유의 ListenHub 자산 URL. 후자는 소유권을 확인한 뒤 짧게 유효한 다운로드 링크를 발급합니다 |
model | string | 아니요 | Suno 모델. 모델 참고. 기본값은 V6 |
customMode | boolean | 아니요 | 기본값은 true. false로 두면 prompt만으로 커버를 만듭니다 |
prompt | string | 커스텀 모드에서 필수 | 새 보컬의 가사. customMode가 true이고 instrumental이 false일 때 필수입니다. 커스텀 모드가 아니면 자유 형식 설명으로 쓰입니다 |
style | string | 커스텀 모드에서 필수 | 음악 스타일 |
title | string | 커스텀 모드에서 필수 | 트랙 제목 |
instrumental | boolean | 아니요 | 보컬 없이 커버를 생성합니다. 기본값은 false |
providerParams | object | 아니요 | Suno 전용 파라미터. Suno 전용 파라미터 참고 |
202와 함께 { "taskId": "...", "status": "pending" }을 반환합니다. /generate와 마찬가지로 Suno 모델 등급에 따라 과금됩니다.
곡 확장
POST /v1/music/extend
기존 곡을 지정한 시점부터 이어서 생성합니다. multipart/form-data로 전송합니다. 원본은 audio, uploadUrl, providerSongId 중 하나로 제공하세요.
curl -X POST "https://api.marswave.ai/openapi/v1/music/extend" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@original.mp3" \
-F "model=mureka-8" \
-F "extendAt=30" \
-F "extendType=tail"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('original.mp3')]), 'original.mp3');
form.append('model', 'mureka-8');
form.append('extendAt', '30');
form.append('extendType', 'tail');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/extend', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Task ID:', data.taskId);import os
import requests
with open('original.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/extend',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
data={'model': 'mureka-8', 'extendAt': '30', 'extendType': 'tail'},
)
data = response.json()['data']
print('Task ID:', data['taskId'])요청 파라미터(Mureka 경로):
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
audio | file | 소스 중 하나 | 오디오 파일(mp3/m4a, 최대 10MB). uploadUrl / providerSongId와 함께 쓸 수 없음 |
uploadUrl | string | 소스 중 하나 | 오디오 URL(접근 가능한 외부 링크 또는 내부 GCS URL) |
providerSongId | string | 소스 중 하나 | 이전 결과에서 얻은 Mureka song id |
model | string | 아니요 | Mureka 모델 이름. 모델 참고. 기본값은 auto이며 이 엔드포인트에서는 mureka-7.6으로 해석됩니다 |
extendAt | number | 예 | 확장을 시작할 시점(초). 8–420 범위여야 합니다 |
extendType | string | 아니요 | tail(뒤로 이어감, 기본값) 또는 head(앞으로 이어감, mureka-8 전용) |
lyrics | string | 예 | 새로 만들 구간의 가사 |
prompt | string | 아니요 | 스타일·설명 |
style | string | 아니요 | 음악 스타일 |
title | string | 아니요 | 트랙 제목 |
instrumental | boolean | 아니요 | 새 구간을 보컬 없이 생성 |
요청 파라미터(Suno 경로):
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
provider | string | 예 | suno. 지정하지 않으면 Mureka 경로로 처리됩니다 |
uploadUrl | string | 예 | 원본 녹음. 공개적으로 접근 가능한 URL이거나 본인 소유의 ListenHub 자산 URL |
continueAt | number | 예 | 이어서 생성할 지점(초). 0보다 커야 합니다 |
model | string | 아니요 | Suno 모델. 모델 참고. 기본값은 V6 |
prompt | string | 아니요 | 이어질 구간의 가사. instrumental이 true면 무시됩니다 |
style | string | 아니요 | 음악 스타일 |
title | string | 아니요 | 트랙 제목 |
instrumental | boolean | 아니요 | 이어질 구간을 보컬 없이 생성 |
negativeTags | string | 아니요 | Suno 전용 파라미터 참고 |
vocalGender | string | 아니요 | Suno 전용 파라미터 참고 |
styleWeight | number | 아니요 | Suno 전용 파라미터 참고 |
weirdnessConstraint | number | 아니요 | Suno 전용 파라미터 참고 |
audioWeight | number | 아니요 | Suno 전용 파라미터 참고 |
model은 크레딧을 예약하기 전에 검증합니다. 모델의 두 표에 없는 값은 그 자리에서 거부됩니다. provider를 명시하면 검증 범위가 해당 provider의 등급으로 좁혀집니다.
두 경로가 공유하는 필드는 uploadUrl, prompt, style, title, instrumental뿐입니다. audio, providerSongId, extendAt, extendType, lyrics는 Mureka 전용이고 continueAt은 Suno 전용입니다.
가사 인식
POST /v1/music/recognize
오디오 파일에서 타임스탬프가 붙은 섹션 단위로 가사를 전사합니다. 동기 방식이라 결과가 응답에 바로 담깁니다. multipart/form-data로 전송합니다. 이것은 녹음에서 가사를 읽어내는 기능입니다. 프롬프트로 새 가사를 쓰려면 가사 생성을 참고하세요.
curl -X POST "https://api.marswave.ai/openapi/v1/music/recognize" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@song.mp3"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('song.mp3')]), 'song.mp3');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/recognize', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Sections:', data.result.lyricsSections.length);import os
import requests
with open('song.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/recognize',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
)
data = response.json()['data']
print('Sections:', len(data['result']['lyricsSections']))요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
audio | file | 예 | 오디오 파일(mp3/m4a, 최대 10MB) |
data.result 객체에는 duration과 lyricsSections 배열이 들어 있습니다.
오디오 설명
POST /v1/music/describe
오디오 파일을 분석해 설명과 함께 태그, 장르, 악기를 반환합니다. 동기 방식입니다. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/describe" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@song.mp3"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('song.mp3')]), 'song.mp3');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/describe', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log(data.result.description, data.result.genres);import os
import requests
with open('song.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/describe',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
)
data = response.json()['data']
print(data['result']['description'], data['result']['genres'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
audio | file | 예 | 분석할 오디오 파일(mp3/m4a, 최대 10MB) |
data.result 객체에는 description, tags, genres, instruments가 들어 있습니다.
스템 분리
POST /v1/music/stem
오디오 파일을 스템(보컬, 베이스, 드럼, 그 외)으로 분리하고 다운로드 URL을 반환합니다. 동기 방식입니다. multipart/form-data로 전송합니다.
curl -X POST "https://api.marswave.ai/openapi/v1/music/stem" \
-H "Authorization: Bearer $LISTENHUB_API_KEY" \
-F "audio=@song.mp3" \
-F "model=audio-separation-1"import { readFile } from 'node:fs/promises';
const form = new FormData();
form.append('audio', new Blob([await readFile('song.mp3')]), 'song.mp3');
form.append('model', 'audio-separation-1');
const response = await fetch('https://api.marswave.ai/openapi/v1/music/stem', {
method: 'POST',
headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` },
body: form,
});
const { data } = await response.json();
console.log('Stems ZIP:', data.result.zipUrl);import os
import requests
with open('song.mp3', 'rb') as f:
response = requests.post(
'https://api.marswave.ai/openapi/v1/music/stem',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
files={'audio': f},
data={'model': 'audio-separation-1'},
)
data = response.json()['data']
print('Stems ZIP:', data['result']['zipUrl'])요청 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
audio | file | 예 | 분리할 오디오 파일(mp3/m4a, 최대 10MB) |
model | string | 아니요 | audio-separation-1(기본값) 또는 audio-separation-2(MIDI도 함께 생성) |
data.result 객체에는 zipUrl, midiZipUrl(audio-separation-2일 때), expiresAt이 들어 있습니다. 다운로드 링크는 생성 후 약 24시간이 지나면 만료됩니다.
작업 목록 조회
GET /v1/music/tasks
내 음악 작업을 최신순으로 나열합니다.
curl -X GET "https://api.marswave.ai/openapi/v1/music/tasks?page=1&pageSize=20&status=success" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"const response = await fetch(
'https://api.marswave.ai/openapi/v1/music/tasks?page=1&pageSize=20',
{ headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` } }
);
const { data } = await response.json();
console.log(`${data.items.length} of ${data.total} tasks`);import os
import requests
response = requests.get(
'https://api.marswave.ai/openapi/v1/music/tasks',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
params={'page': 1, 'pageSize': 20},
)
data = response.json()['data']
print(len(data['items']), 'of', data['total'], 'tasks')쿼리 파라미터:
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
page | integer | 아니요 | 페이지 번호, 최소 1. 기본값은 1 |
pageSize | integer | 아니요 | 페이지당 항목 수, 1–100. 기본값은 20 |
status | string | 아니요 | pending, generating, uploading, success, failed로 필터링 |
data는 배열이 아니라 페이지입니다. items에 이 페이지의 작업이, total에 전체 개수가 담기고, page와 pageSize는 실제로 적용된 값을 돌려줍니다.
작업 조회
GET /v1/music/tasks/{taskId}
작업 하나를 가져옵니다. 비동기 생성 요청을 보낸 뒤 폴링하는 엔드포인트가 바로 이것입니다.
curl -X GET "https://api.marswave.ai/openapi/v1/music/tasks/{taskId}" \
-H "Authorization: Bearer $LISTENHUB_API_KEY"const response = await fetch(
`https://api.marswave.ai/openapi/v1/music/tasks/${taskId}`,
{ headers: { 'Authorization': `Bearer ${process.env.LISTENHUB_API_KEY}` } }
);
const { data } = await response.json();
console.log('Status:', data.status);
if (data.status === 'success') console.log('Audio:', data.tracks[0].audioUrl);import os
import requests
response = requests.get(
f'https://api.marswave.ai/openapi/v1/music/tasks/{task_id}',
headers={'Authorization': f'Bearer {os.environ["LISTENHUB_API_KEY"]}'},
)
data = response.json()['data']
print('Status:', data['status'])
if data['status'] == 'success':
print('Audio:', data['tracks'][0]['audioUrl'])작업 응답 필드:
| 필드 | 타입 | 설명 |
|---|---|---|
id | string | 작업 ID |
provider | string | mureka 또는 suno. 생성 시 이미 해석되므로 default로 저장되지 않습니다 |
taskType | string | GENERATE, INSTRUMENTAL, REMIX, EXTEND, COVER, SOUNDTRACK, TRACK, RECOGNIZE, DESCRIBE, STEM, REGION_EDIT |
status | string | pending, generating, uploading, success, failed |
params | object | 생성 요청 내용을 그대로 반환. model에는 작업이 실행된 등급이 담깁니다 |
tracks | array | 완성된 트랙: title, tags, duration(초), 서명된 audioUrl |
creditCost | number | 소모한 크레딧 |
errorMessage | string | 실패 사유(status가 failed일 때만) |
createdAt | number | 생성 시각(밀리초 타임스탬프) |
updatedAt | number | 최종 수정 시각(밀리초 타임스탬프) |
크레딧
/generate, /instrumental, /cover는 모델 등급에 따라 과금됩니다(모델 참고). 나머지는 고정 요금입니다: /remix 10, /extend는 mureka-7.6과 auto(mureka-7.6으로 해석됩니다)가 10·그 외 모델은 15, /soundtrack 15, /track 15, /describe 15, /recognize 3, /stem은 audio-separation-1이 10·audio-separation-2가 100.
비동기 생성의 경우 요청 시점에 크레딧을 예약하고, success가 되면 확정하며, 실패하면 자동으로 환불합니다. 정확한 비용은 작업마다 creditCost로 반환됩니다(분석 호출에서는 결과의 creditCost). 실시간 잔액은 GET /v1/user/subscription으로 확인하고, 크레딧과 기능의 대응 관계는 요금을 참고하세요.
SDK 및 CLI
공식 SDK와 CLI는 아래 엔드포인트를 비동기 폴링까지 포함해 감싸 줍니다. 둘 다 아직 provider 선택과 Suno 전용 파라미터는 지원하지 않으므로, 그 두 가지는 HTTP로 직접 호출하세요.
JavaScript SDK
OpenAPIClient.createMusicGenerate / createMusicCover / createMusicInstrumental / createMusicSoundtrack / createMusicTrack / createMusicRemix, 그리고 recognizeMusic / describeMusic / stemMusic 및 getMusicTask / listMusicTasks.
CLI
listenhub openapi music generate | instrumental | soundtrack | track | remix | recognize | describe | stem | list | get — 폴링 제어용 --no-wait 및 --timeout 지원.