Voice Studio API · v1

감정까지 연기하는
한국어 음성 API

OpenAI SDK를 그대로 쓰고 base_url만 바꾸면 됩니다. 대본에 [화남 0.9] 같은 태그를 붙이면 그 구간만 화난 목소리로 읽습니다.

첫 소리까지
0.1초
한국어 글자 오류율
0.9%
감정
8가지
POST /v1/audio/speech
import fs from "node:fs";
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://voicestudio.gensapps.com/v1",
  apiKey: process.env.VOICE_STUDIO_KEY,
});

const audio = await client.audio.speech.create({
  model: "voice-studio-1",
  voice: "minji",
  input: "[차분]괜찮아.[/차분] [놀람 0.7]잠깐, 저 소리 들었어?[/놀람] [화남 0.9]누가 문을 열어 둔 거야![/화남]",
  instructions: "긴장한 밤 장면. 낮은 목소리로.",
  response_format: "mp3",
  markup: "emotion-v1", // 감정 표기 켜기
});

fs.writeFileSync("scene.mp3", Buffer.from(await audio.arrayBuffer()));
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://voicestudio.gensapps.com/v1",
    api_key=os.environ["VOICE_STUDIO_KEY"],
)

with client.audio.speech.with_streaming_response.create(
    model="voice-studio-1",
    voice="minji",
    input="[차분]괜찮아.[/차분] [놀람 0.7]잠깐, 저 소리 들었어?[/놀람] [화남 0.9]누가 문을 열어 둔 거야![/화남]",
    instructions="긴장한 밤 장면. 낮은 목소리로.",
    response_format="mp3",
    extra_body={"markup": "emotion-v1"},  # 감정 표기 켜기
) as audio:
    audio.stream_to_file("scene.mp3")
목차

빠른 시작

  1. API 키 받기가입한 뒤 조직 설정에서 키를 만듭니다. 키는 만들 때 한 번만 보입니다.
  2. SDK 설치OpenAI 공식 SDK를 그대로 씁니다.
  3. 첫 음성 만들기아래 예제를 그대로 실행합니다. 키는 환경 변수 VOICE_STUDIO_KEY에서 읽습니다.
터미널
npm install openai
export VOICE_STUDIO_KEY="vs-..."
pip install openai
export VOICE_STUDIO_KEY="vs-..."

인증

모든 요청에 Authorization: Bearer <API 키> 헤더를 붙입니다. 기본 주소는 https://voicestudio.gensapps.com/v1입니다. 키는 서버에만 두고 브라우저 코드에 넣지 않습니다.

POST/v1/audio/speech

음성 만들기

대본 하나를 한 목소리로 읽습니다. 응답 본문은 음성 파일입니다.

필드형식설명
model문자열voice-studio-1
voice문자열목소리 id. 목소리 목록에서 찾습니다. 감정 강화(베타)는 minji@preview처럼 id 뒤에 @preview를 붙입니다
input문자열읽을 대본(2,000자까지). input과 script 중 하나를 보냅니다
script객체 · 선택줄마다 목소리를 정하는 JSON 대본. JSON 대본에서 설명합니다
markup문자열 · 선택emotion-v1 · emotion-v2이면 대괄호 태그를 감정으로 읽습니다. 없으면 태그도 글자 그대로 읽습니다
instructions문자열 · 선택전체 말투(500자까지). 구간 태그가 있으면 그 구간은 태그를 따릅니다
response_format문자열 · 선택mp3 · wav(기본 mp3)

생성한 음성에는 들리지 않는 워터마크가 들어갑니다. AI 기본법의 생성물 표시 의무를 따릅니다.

scene.mp3 만들기
import fs from "node:fs";
import OpenAI from "openai";

const client = new OpenAI({
  baseURL: "https://voicestudio.gensapps.com/v1",
  apiKey: process.env.VOICE_STUDIO_KEY,
});

const audio = await client.audio.speech.create({
  model: "voice-studio-1",
  voice: "minji",
  input: "[차분]괜찮아.[/차분] [놀람 0.7]잠깐, 저 소리 들었어?[/놀람] [화남 0.9]누가 문을 열어 둔 거야![/화남]",
  instructions: "긴장한 밤 장면. 낮은 목소리로.",
  response_format: "mp3",
  markup: "emotion-v1", // 감정 표기 켜기
});

fs.writeFileSync("scene.mp3", Buffer.from(await audio.arrayBuffer()));
import os
from openai import OpenAI

client = OpenAI(
    base_url="https://voicestudio.gensapps.com/v1",
    api_key=os.environ["VOICE_STUDIO_KEY"],
)

with client.audio.speech.with_streaming_response.create(
    model="voice-studio-1",
    voice="minji",
    input="[차분]괜찮아.[/차분] [놀람 0.7]잠깐, 저 소리 들었어?[/놀람] [화남 0.9]누가 문을 열어 둔 거야![/화남]",
    instructions="긴장한 밤 장면. 낮은 목소리로.",
    response_format="mp3",
    extra_body={"markup": "emotion-v1"},  # 감정 표기 켜기
) as audio:
    audio.stream_to_file("scene.mp3")

감정 표기 emotion-v1

태그가 없으면 글 그대로 읽습니다. 구간을 대괄호 태그로 감싸면 그 구간만 그 감정으로 읽고, 한 문장 안에서도 감정을 여러 번 바꿀 수 있습니다.

형식의미예시
[감정]…[/감정]구간 감정[기쁨]드디어 왔어요![/기쁨]
[감정 세기]…[/감정]세기는 0~1, 생략하면 0.6. 지금은 세기 값에 따른 소리 차이를 보장하지 않습니다[화남 0.9]나가![/화남]
감정 이름 차분 calm기쁨 joy슬픔 sad화남 angry놀람 surprised속삭임 whisper두려움 fear진지 serious

한국어와 영어 이름을 모두 받습니다. 구간 안에 다른 감정 구간을 넣을 수 없습니다. 지원하지 않는 감정이나 닫히지 않은 태그는 무시하지 않고 오류로 돌려줍니다.

curl
curl https://voicestudio.gensapps.com/v1/audio/speech \
  -H "Authorization: Bearer $VOICE_STUDIO_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model": "voice-studio-1", "voice": "minji", "markup": "emotion-v1",
       "input": "[기쁨]드디어 도착했어요![/기쁨] 오늘 정말 길었네요."}' \
  -o hello.mp3
GET/v1/voices

목소리 목록

쓸 수 있는 목소리의 id · 이름 · 성별 · 나이대 · 음색을 돌려줍니다.

목소리 목록
const res = await fetch("https://voicestudio.gensapps.com/v1/voices", {
  headers: { Authorization: `Bearer ${process.env.VOICE_STUDIO_KEY}` },
});
for (const v of (await res.json()).data) console.log(v.id, v.name, v.tone);
import os, httpx

res = httpx.get(
    "https://voicestudio.gensapps.com/v1/voices",
    headers={"Authorization": f"Bearer {os.environ['VOICE_STUDIO_KEY']}"},
)
for v in res.json()["data"]:
    print(v["id"], v["name"], v["tone"])

오류

오류는 {"error": {"type", "message"}} 형식의 JSON으로 돌려줍니다.

코드의미조치
400 invalid_markup지원하지 않는 감정 · 닫히지 않은 태그. 몇 번째 글자인지 position으로 알려 줍니다표기를 고쳐 다시 보냅니다
401API 키가 없거나 맞지 않습니다키를 확인합니다
402크레딧이 부족합니다조직관리자에게 충전을 요청합니다
429한도를 넘었습니다Retry-After 초만큼 기다린 뒤 다시 보냅니다
503처리 용량이 잠시 부족합니다Retry-After 뒤 다시 보냅니다

API는 준비 중입니다. 위 형식은 설계안이며 공개 전에 바뀔 수 있습니다.

상세 사용법은 충전한 조직에 공개합니다

JSON 대본으로 줄마다 목소리를 바꾸는 방법, 빠르기 · 강조 같은 세부 표기, 대본 오류 코드를 볼 수 있습니다.

가입하고 보기