ListenHubOpenAPI
API 레퍼런스

음악 생성

Mureka로 노래, 인스트루멘털, 사운드트랙을 생성하고 기존 오디오를 분석합니다 — 가사 인식, 오디오 설명, 스템 분리.

Music API는 텍스트, 가사, 이미지, 참조 오디오를 음악으로 바꾸고, 기존 트랙을 분석합니다. 생성 기능은 Mureka가 제공합니다. 모든 엔드포인트는 https://api.marswave.ai/openapi/v1/music 아래에 있으며 Authorization: Bearer $LISTENHUB_API_KEY로 인증합니다.

이 API는 두 가지 응답 패턴으로 나뉩니다.

패턴엔드포인트결과를 받는 방식
비동기 생성/generate, /instrumental, /soundtrack, /track, /remix, /extend202taskId를 반환합니다. statussuccess가 될 때까지 GET /v1/music/tasks/{taskId}를 폴링(polling)합니다.
동기 분석/recognize, /describe, /stem200을 반환하고 결과가 같은 응답에 담깁니다. 폴링이 필요 없습니다.

모든 응답은 { "code": 0, "message": "", "data": { ... } } 형태로 감싸집니다. code가 0이 아니면 오류입니다 — 오류 처리를 참고하세요. 아래 예제는 모두 data에서 필드를 읽습니다.

모델

생성 엔드포인트는 프로바이더 비의존(provider-neutral) 계약을 따르는 model 파라미터를 받습니다. Mureka 모델은 다음과 같습니다.

모델설명
auto기본값. 서비스가 모델을 자동으로 선택합니다.
mureka-7.6
mureka-8
mureka-9/instrumental에서는 사용할 수 없습니다.
mureka-o2

스템 분리(/stem)는 다른 모델 세트를 사용합니다: audio-separation-1(기본값) 또는 audio-separation-2(MIDI도 함께 생성).

비동기 작업 라이프사이클

  1. 생성 요청을 보냅니다. 응답에는 taskId와 초기 statuspending이 담깁니다.
  2. GET /v1/music/tasks/{taskId}를 폴링합니다. statuspendinggeneratinguploadingsuccess 순으로 진행됩니다.
  3. success가 되면 완성된 tracks 배열(제목, 태그, 길이, 서명된 audioUrl)을 읽습니다. failed인 경우에는 errorMessage를 읽습니다.

권장 폴링 방식: 요청을 보낸 뒤 약 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.ai/.../track-1.mp3"
      }
    ],
    "creditCost": 20,
    "createdAt": 1730000000000,
    "updatedAt": 1730000180000
  }
}

트랙의 audioUrl 값은 서명된 URL이며 작업을 조회한 시점으로부터 약 1시간 뒤에 만료됩니다. 만료 전에 오디오를 내려받거나 작업을 다시 조회해 URL을 갱신하세요.

텍스트 또는 가사로 생성

POST /v1/music/generate

스타일 프롬프트와 가사(둘 중 하나 또는 둘 다)로 노래를 만듭니다. JSON을 전송합니다. Mureka에서는 인스트루멘털이 아닌 요청에 lyrics를 포함해야 합니다.

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'])

요청 파라미터:

필드타입필수설명
promptstring아니요스타일·설명 프롬프트
lyricsstring아니요가사. Mureka에서 인스트루멘털이 아닐 때 필수
stylestring아니요스타일 태그. prompt의 대체값으로 사용됨
titlestring아니요트랙 제목
instrumentalboolean아니요보컬 없이 생성
modelstring아니요모델 참고. 기본값은 auto
vocalIdstring아니요기존 Mureka vocal id 재사용
providerstring아니요default(Mureka), mureka, suno 중 하나. 기본값은 default
providerParamsobject아니요프로바이더별 파라미터

202와 함께 { "taskId": "...", "status": "pending" }을 반환합니다. 결과는 작업을 폴링해서 가져옵니다.

인스트루멘털 생성

POST /v1/music/instrumental

텍스트 프롬프트 또는 참조 오디오 파일로 단독 인스트루멘털을 만듭니다. promptreferenceAudio 중 정확히 하나만 제공하세요. 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'])

요청 파라미터:

필드타입필수설명
promptstring둘 중 하나스타일·장르 설명. referenceAudio와 함께 쓸 수 없음
referenceAudiofile둘 중 하나참조 오디오(mp3/m4a, 최대 10MB). prompt와 함께 쓸 수 없음
modelstring아니요auto, mureka-7.6, mureka-8, mureka-o2 중 하나. 기본값은 auto

이미지 또는 영상으로 사운드트랙 생성

POST /v1/music/soundtrack

이미지나 영상에 어울리는 음악을 생성합니다. imagevideo 중 정확히 하나만 제공하세요. 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'])

요청 파라미터:

필드타입필수설명
imagefile둘 중 하나이미지(jpg/jpeg/png/webp). video와 함께 쓸 수 없음
videofile둘 중 하나영상(mp4/mov/avi/mkv/webm). image와 함께 쓸 수 없음
promptstring아니요스타일·설명 프롬프트
modelstring아니요auto, mureka-7.6, mureka-8, mureka-9, mureka-o2 중 하나. 기본값은 auto

단일 트랙 생성

POST /v1/music/track

참조 오디오 파일 또는 기존 Mureka providerSongId로 악기 트랙이나 보컬 트랙 하나를 생성합니다. audioproviderSongId 중 정확히 하나만 제공하세요. 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'])

요청 파라미터:

필드타입필수설명
generateTypestring생성할 트랙 종류. Vocals, Instrumental, Drums, Bass, Guitar, Keyboard, Percussion, Strings, Synth, FX, Brass, Woodwinds 중 하나
promptstring스타일·장르 설명
audiofile둘 중 하나참조 오디오(mp3/m4a/wav, 최대 10MB). providerSongId와 함께 쓸 수 없음
providerSongIdstring둘 중 하나이전 결과에서 얻은 Mureka song id. audio와 함께 쓸 수 없음
lyricsstringVocals일 때가사. generateTypeVocals이면 필수
vocalGenderstring아니요male 또는 female. generateType=Vocals일 때만 사용
generateStartnumber아니요구간 시작 지점(초)
generateEndnumber아니요구간 종료 지점(초)

기존 곡 리믹스

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'])

요청 파라미터:

필드타입필수설명
lyricsstring새 가사
promptstring스타일·장르 설명
audiofile소스 중 하나오디오 파일(mp3/m4a, 최대 10MB)
audioUrlstring소스 중 하나ListenHub 내부 오디오 URL(본인 소유이거나 공개 상태여야 함)
providerSongIdstring소스 중 하나이전 결과에서 얻은 Mureka song id

곡 확장

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 경로):

필드타입필수설명
audiofile소스 중 하나오디오 파일(mp3/m4a, 최대 10MB). uploadUrl / providerSongId와 함께 쓸 수 없음
uploadUrlstring소스 중 하나오디오 URL(접근 가능한 외부 링크 또는 내부 GCS URL)
providerSongIdstring소스 중 하나이전 결과에서 얻은 Mureka song id
modelstring아니요모델 참고
extendAtnumber아니요확장을 시작할 시점(초, 8–420)
extendTypestring아니요tail(뒤로 이어감, 기본값) 또는 head(앞으로 이어감, mureka-8 전용)
lyricsstring아니요새로 만들 구간의 가사
promptstring아니요스타일·설명
stylestring아니요음악 스타일
titlestring아니요트랙 제목
instrumentalboolean아니요새 구간을 보컬 없이 생성

/extendcontinueAt, uploadUrl, Suno 모델 버전을 사용하는 Suno 경로도 지원합니다. provider=suno와 Suno 전용 필드를 전달하세요. 기본 프로바이더는 Mureka입니다.

가사 인식

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']))

요청 파라미터:

필드타입필수설명
audiofile오디오 파일(mp3/m4a, 최대 10MB)

data.result 객체에는 durationlyricsSections 배열이 들어 있습니다.

오디오 설명

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'])

요청 파라미터:

필드타입필수설명
audiofile분석할 오디오 파일(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'])

요청 파라미터:

필드타입필수설명
audiofile분리할 오디오 파일(mp3/m4a, 최대 10MB)
modelstring아니요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.length} 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},
)
print(len(response.json()['data']), 'tasks')

쿼리 파라미터:

필드타입필수설명
pageinteger아니요페이지 번호, 최소 1. 기본값은 1
pageSizeinteger아니요페이지당 항목 수, 1100. 기본값은 20
statusstring아니요pending, generating, uploading, success, failed로 필터링

작업 조회

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'])

작업 응답 필드:

필드타입설명
idstring작업 ID
providerstringdefault, mureka, suno 중 하나
taskTypestringGENERATE, INSTRUMENTAL, REMIX, EXTEND, COVER
statusstringpending, generating, uploading, success, failed
paramsobject생성 요청 내용을 그대로 반환
tracksarray완성된 트랙: title, tags, duration(초), 서명된 audioUrl
creditCostnumber소모한 크레딧
errorMessagestring실패 사유(statusfailed일 때만)
createdAtnumber생성 시각(밀리초 타임스탬프)
updatedAtnumber최종 수정 시각(밀리초 타임스탬프)

크레딧

각 엔드포인트는 모델 등급에 따라 크레딧을 소모합니다. 비동기 생성의 경우 요청 시점에 크레딧을 예약하고, success가 되면 확정하며, 실패하면 자동으로 환불합니다. 정확한 비용은 작업마다 creditCost로 반환됩니다(분석 호출에서는 결과의 creditCost). 실시간 잔액은 GET /v1/user/subscription으로 확인하고, 크레딧과 기능의 대응 관계는 요금을 참고하세요.

SDK 및 CLI

공식 SDK와 CLI는 이 페이지의 모든 엔드포인트를 비동기 폴링까지 포함해 감싸 줍니다.

이 페이지의 내용