ListenHubDocs
API 레퍼런스

음악 생성

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, /extend202와 taskId를 반환합니다. status가 success가 될 때까지 GET /v1/music/tasks/{taskId}를 폴링(polling)합니다.
동기 분석/recognize, /describe, /stem200을 반환하고 결과가 같은 응답에 담깁니다. 폴링이 필요 없습니다.

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

프로바이더

provider는 mureka, suno, default를 받으며 default는 Mureka로 해석됩니다. 이 필드를 읽는 엔드포인트는 둘뿐이고, 나머지는 한 프로바이더가 전담하므로 값을 넘겨도 무시합니다.

엔드포인트MurekaSuno프로바이더 결정 방식
/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 모델:

모델크레딧설명
auto10Mureka 기본값. 서비스가 모델을 자동으로 선택합니다.
mureka-7.65
mureka-810
mureka-910/instrumental에서는 사용할 수 없습니다.
mureka-o210

Suno 모델:

모델크레딧설명
V415
V4_515
V4_5PLUS15
V4_5ALL15
V515
V5_520
V620Suno 기본값. 가장 최신 등급입니다.

모든 등급은 계속 명시적으로 지정할 수 있습니다. model을 생략한 Suno 요청은 V6로 실행됩니다.

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

Suno 전용 파라미터

Suno에는 Mureka에 대응 항목이 없는 파라미터가 일곱 개 있습니다. /generate와 /cover에서는 providerParams 안에 넣고, /extend에서는 최상위 폼 필드로 넘깁니다.

필드타입적용 엔드포인트설명
negativeTagsstring/generate, /cover, /extend피하고 싶은 스타일 태그, 쉼표로 구분
vocalGenderstring/generate, /cover, /extendm 또는 f. male / female을 받는 /track과 다르니 주의하세요
styleWeightnumber/generate, /cover, /extend0–1. 결과가 style을 얼마나 충실히 따를지
weirdnessConstraintnumber/generate, /cover, /extend0–1. 결과가 통상적인 형태에서 얼마나 벗어날 수 있는지
audioWeightnumber/generate, /cover, /extend0–1. 원본 녹음의 가중치. 원본 오디오를 함께 보내는 요청에서만 의미가 있습니다
personaIdstring/generate, /cover트랙을 부를 Suno persona. /cover에서는 커스텀 모드에서만 적용됩니다
personaModelstring/generate, /cover해당 persona를 만든 모델 등급. personaId와 함께 보내세요

위 목록에 없는 키는 요청이 Suno에 도달하기 전에 providerParams에서 제거됩니다.

비동기 작업 라이프사이클

  1. 생성 요청을 보냅니다. 응답에는 taskId와 초기 status인 pending이 담깁니다.
  2. GET /v1/music/tasks/{taskId}를 폴링합니다. status는 pending → generating → uploading → success 순으로 진행됩니다.
  3. 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'])

요청 파라미터:

필드타입필수설명
promptstring아니요Mureka에서는 스타일·설명 프롬프트. Suno는 이 값을 가사로 읽으므로 lyrics를 쓰고 스타일은 style로 넘기세요
lyricsstring아니요가사. instrumental이 true가 아니면 Mureka에서는 필수. Suno는 이 값을 곡의 가사로 사용합니다
stylestring아니요스타일 태그. prompt의 대체값으로 사용됨
titlestring아니요트랙 제목
instrumentalboolean아니요보컬 없이 생성
customModeboolean아니요Suno 전용. 기본값은 true이며 이때 style과 title이 설명을 담고 가사 본문은 lyrics에서 가져옵니다. false로 두면 곡 전체를 prompt 하나로 생성합니다
modelstring아니요모델 참고. 기본값은 Mureka auto, Suno V6
vocalIdstring아니요기존 Mureka vocal id 재사용. Mureka 전용
providerstring아니요default(Mureka), mureka, suno 중 하나. 기본값은 default
providerParamsobject아니요프로바이더별 파라미터. 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'])

요청 파라미터:

필드타입필수설명
promptstring둘 중 하나스타일·장르 설명. referenceAudio와 함께 쓸 수 없음
referenceAudiofile둘 중 하나참조 오디오(mp3/m4a, 최대 10MB). prompt와 함께 쓸 수 없음
modelstring아니요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'])

요청 파라미터:

필드타입필수설명
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로 악기 트랙이나 보컬 트랙 하나를 생성합니다. 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'])

요청 파라미터:

필드타입필수설명
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일 때가사. generateType이 Vocals이면 필수
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/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'])

요청 파라미터:

필드타입필수설명
uploadUrlstring예원본 녹음. 공개적으로 접근 가능한 URL이거나 본인 소유의 ListenHub 자산 URL. 후자는 소유권을 확인한 뒤 짧게 유효한 다운로드 링크를 발급합니다
modelstring아니요Suno 모델. 모델 참고. 기본값은 V6
customModeboolean아니요기본값은 true. false로 두면 prompt만으로 커버를 만듭니다
promptstring커스텀 모드에서 필수새 보컬의 가사. customMode가 true이고 instrumental이 false일 때 필수입니다. 커스텀 모드가 아니면 자유 형식 설명으로 쓰입니다
stylestring커스텀 모드에서 필수음악 스타일
titlestring커스텀 모드에서 필수트랙 제목
instrumentalboolean아니요보컬 없이 커버를 생성합니다. 기본값은 false
providerParamsobject아니요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 경로):

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

요청 파라미터(Suno 경로):

필드타입필수설명
providerstring예suno. 지정하지 않으면 Mureka 경로로 처리됩니다
uploadUrlstring예원본 녹음. 공개적으로 접근 가능한 URL이거나 본인 소유의 ListenHub 자산 URL
continueAtnumber예이어서 생성할 지점(초). 0보다 커야 합니다
modelstring아니요Suno 모델. 모델 참고. 기본값은 V6
promptstring아니요이어질 구간의 가사. instrumental이 true면 무시됩니다
stylestring아니요음악 스타일
titlestring아니요트랙 제목
instrumentalboolean아니요이어질 구간을 보컬 없이 생성
negativeTagsstring아니요Suno 전용 파라미터 참고
vocalGenderstring아니요Suno 전용 파라미터 참고
styleWeightnumber아니요Suno 전용 파라미터 참고
weirdnessConstraintnumber아니요Suno 전용 파라미터 참고
audioWeightnumber아니요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']))

요청 파라미터:

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

요청 파라미터:

필드타입필수설명
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.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')

쿼리 파라미터:

필드타입필수설명
pageinteger아니요페이지 번호, 최소 1. 기본값은 1
pageSizeinteger아니요페이지당 항목 수, 1–100. 기본값은 20
statusstring아니요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'])

작업 응답 필드:

필드타입설명
idstring작업 ID
providerstringmureka 또는 suno. 생성 시 이미 해석되므로 default로 저장되지 않습니다
taskTypestringGENERATE, INSTRUMENTAL, REMIX, EXTEND, COVER, SOUNDTRACK, TRACK, RECOGNIZE, DESCRIBE, STEM, REGION_EDIT
statusstringpending, generating, uploading, success, failed
paramsobject생성 요청 내용을 그대로 반환. model에는 작업이 실행된 등급이 담깁니다
tracksarray완성된 트랙: title, tags, duration(초), 서명된 audioUrl
creditCostnumber소모한 크레딧
errorMessagestring실패 사유(status가 failed일 때만)
createdAtnumber생성 시각(밀리초 타임스탬프)
updatedAtnumber최종 수정 시각(밀리초 타임스탬프)

크레딧

/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로 직접 호출하세요.

이 페이지의 내용